doc: clean up syntax highlighting
[charm.git] / doc / tcharm / manual.rst
1 =========================
2 Threaded Charm++ (TCharm)
3 =========================
4
5 .. contents::
6    :depth: 3
7
8 Motivation
9 ==========
10
11 Charm++ includes several application frameworks, such as the Finite
12 Element Framework, the Multiblock Framework, and AMPI. These frameworks
13 do almost all their work in load balanced, migratable threads.
14
15 The Threaded Charm++ Framework, TCharm, provides both common runtime
16 support for these threads and facilities for combining multiple
17 frameworks within a single program. For example, you can use TCharm to
18 create a Finite Element Framework application that also uses AMPI to
19 communicate between Finite Element chunks.
20
21 Specifically, TCharm provides language-neutral interfaces for:
22
23 #. Program startup, including read-only global data setup and the
24    configuration of multiple frameworks.
25
26 #. Run-time load balancing, including migration.
27
28 #. Program shutdown.
29
30 The first portion of this manual describes the general properties of
31 TCharm common to all the application frameworks, such as program
32 contexts and how to write migratable code. The second portion describes
33 in detail how to combine separate frameworks into a single application.
34
35 Basic TCharm Programming
36 ========================
37
38 Any routine in a TCharm program runs in one of two contexts:
39
40 Serial Context
41    Routines that run on only one processor and with only one set of
42    data. There are absolutely no limitations on what a serial context
43    routine can do—it is as if the code were running in an ordinary
44    serial program. Startup and shutdown routines usually run in the
45    serial context.
46
47 Parallel Context
48    Routines that run on several processors, and may run with several
49    different sets of data on a single processor. This kind of routine
50    must obey certain restrictions. The program’s main computation
51    routines always run in the parallel context.
52
53 Parallel context routines run in a migratable, user-level thread
54 maintained by TCharm. Since there are normally several of these threads
55 per processor, any code that runs in the parallel context has to be
56 thread-safe. However, TCharm is non-preemptive, so it will only switch
57 threads when you make a blocking call, like “MPI_Recv" or
58 “FEM_Update_field".
59
60 .. _sec:global:
61
62 Global Variables
63 ----------------
64
65 By “global variables”, we mean anything that is stored at a fixed,
66 preallocated location in memory. In C, this means variables declared at
67 file scope or with the static keyword. In Fortran, this is either
68 variables that are part of a COMMON block, declared inside a MODULE or
69 variables with the SAVE attribute.
70
71 Global variables are shared by all the threads on a processor, which
72 makes using global variables extremely error prone. To see why this is a
73 problem, consider a program fragment like:
74
75 .. code-block:: fortran
76
77      foo=a
78      call MPI_Recv(...)
79      b=foo
80
81 After this code executes, we might expect b to always be equal to a. but
82 if foo is a global variable, MPI_Recv may block and foo could be changed
83 by another thread.
84
85 For example, if two threads execute this program, they could interleave
86 like:
87
88 ================= =================
89 *Thread 1*        *Thread 2*
90 ================= =================
91 foo=1
92 block in MPI_Recv
93 \                 foo=2
94 \                 block in MPI_Recv
95 b=foo
96 ================= =================
97
98 At this point, thread 1 might expect b to be 1; but it will actually be
99 2. From the point of view of thread 1, the global variable foo suddenly
100 changed its value during the call to MPI_Recv.
101
102 There are several possible solutions to this problem:
103
104 -  Never use global variables—only use parameters or local variables.
105    This is the safest and most general solution. One standard practice
106    is to collect all the globals into a C struct or Fortran type named
107    “Globals", and pass a pointer to this object to all your subroutines.
108    This also combines well with the pup method for doing migration-based
109    load balancing, as described in Section :numref:`sec_pup`.
110
111 -  Never write *different* values to global variables. If every thread
112    writes the same value, global variables can be used safely. For
113    example, you might store some parameters read from a configuration
114    file like the simulation timestep :math:`\Delta t`. See
115    Section :numref:`sec_readonlyglobal` for another, more convenient
116    way to set such variables.
117
118 -  Never issue a blocking call while your global variables are set. This
119    will not work on a SMP version of Charm++, where several processors
120    may share a single set of global variables. Even on a non-SMP
121    version, this is a dangerous solution, because someday someone might
122    add a blocking call while the variables are set. This is only a
123    reasonable solution when calling legacy code or using old serial
124    libraries that might use global variables.
125
126 The above only applies to routines that run in the parallel context.
127 There are no restrictions on global variables for serial context code.
128
129 .. _sec:io:
130
131 Input/Output
132 ------------
133
134 In the parallel context, there are several limitations on open files.
135 First, several threads may run on one processor, so Fortran Logical Unit
136 Numbers are shared by all the threads on a processor. Second, open files
137 are left behind when a thread migrates to another processor—it is a
138 crashing error to open a file, migrate, then try to read from the file.
139
140 Because of these restrictions, it is best to open files only when
141 needed, and close them as soon as possible. In particular, it is best if
142 there are no open files whenever you make blocking calls.
143
144 .. _sec:migration:
145 .. _sec:isomalloc:
146
147 Migration-Based Load Balancing
148 ------------------------------
149
150 The Charm++ runtime framework includes an automatic run-time load
151 balancer, which can monitor the performance of your parallel program. If
152 needed, the load balancer can “migrate” threads from heavily-loaded
153 processors to more lightly-loaded processors, improving the load balance
154 and speeding up the program. For this to be useful, you need to pass the
155 link-time argument -balancer B to set the load balancing algorithm, and
156 the run-time argument +vp N (use N virtual processors) to set the number
157 of threads. The ideal number of threads per processor depends on the
158 problem, but we’ve found five to a hundred threads per processor to be a
159 useful range.
160
161 When a thread migrates, all its data must be brought with it. “Stack
162 data”, such as variables declared locally in a subroutine, will be
163 brought along with the thread automatically. Global data, as described
164 in Section :numref:`sec:global`, is never brought with the thread and
165 should generally be avoided.
166
167 “Heap data” in C is structures and arrays allocated using malloc or new;
168 in Fortran, heap data is TYPEs or arrays allocated using ALLOCATE. To
169 bring heap data along with a migrating thread, you have two choices:
170 write a pup routine or use isomalloc. Pup routines are described in
171 Section :numref:`sec_pup`.
172
173 *Isomalloc* is a special mode which controls the allocation of heap
174 data. You enable isomalloc allocation using the link-time flag “-memory
175 isomalloc”. With isomalloc, migration is completely transparent—all your
176 allocated data is automatically brought to the new processor. The data
177 will be unpacked at the same location (the same virtual addresses) as it
178 was stored originally; so even cross-linked data structures that contain
179 pointers still work properly.
180
181 The limitations of isomalloc are:
182
183 -  Wasted memory. Isomalloc uses a special interface [1]_ to acquire
184    memory, and the finest granularity that can be acquired is one page,
185    typically 4KB. This means if you allocate a 2-entry array, isomalloc
186    will waste an entire 4KB page. We should eventually be able to reduce
187    this overhead for small allocations.
188
189 -  Limited space on 32-bit machines. Machines where pointers are 32 bits
190    long can address just 4GB (:math:`2^{32}` bytes) of virtual address
191    space. Additionally, the operating system and conventional heap
192    already use a significant amount of this space; so the total virtual
193    address space available is typically under 1GB. With isomalloc, all
194    processors share this space, so with just 20 processors the amount of
195    memory per processor is limited to under 50MB! This is an inherent
196    limitation of 32-bit machines; to run on more than a few processors
197    you must use 64-bit machines or avoid isomalloc.
198
199 Advanced TCharm Programming
200 ===========================
201
202 The preceding features are enough to write simple programs that use
203 TCharm-based frameworks. These more advanced techniques provide the user
204 with additional capabilities or flexibility.
205
206 .. _sec_pup:
207
208 Writing a Pup Routine
209 ---------------------
210
211 The runtime system can automatically move your thread stack to the new
212 processor, but unless you use isomalloc, you must write a pup routine to
213 move any global or heap-allocated data to the new processor. A pup
214 (Pack/UnPack) routine can perform both packing (converting your data
215 into a network message) and unpacking (converting the message back into
216 your data). A pup routine is passed a pointer to your data block and a
217 special handle called a “pupper”, which contains the network message.
218
219 In a pup routine, you pass all your heap data to routines named pup_type
220 or fpup_type, where type is either a basic type (such as int, char,
221 float, or double) or an array type (as before, but with a “s” suffix).
222 Depending on the direction of packing, the pupper will either read from
223 or write to the values you pass- normally, you shouldn’t even know
224 which. The only time you need to know the direction is when you are
225 leaving a processor, or just arriving. Correspondingly, the pupper
226 passed to you may be deleting (indicating that you are leaving the
227 processor, and should delete your heap storage after packing), unpacking
228 (indicating you’ve just arrived on a processor, and should allocate your
229 heap storage before unpacking), or neither (indicating the system is
230 merely sizing a buffer, or checkpointing your values).
231
232 pup functions are much easier to write than explain- a simple C heap
233 block and the corresponding pup function is:
234
235 .. code-block:: c++
236
237         typedef struct {
238           int n1;/*Length of first array below*/
239           int n2;/*Length of second array below*/
240           double *arr1; /*Some doubles, allocated on the heap*/
241           int *arr2; /*Some ints, allocated on the heap*/
242         } my_block;
243
244         void pup_my_block(pup_er p,my_block *m)
245         {
246           if (pup_isUnpacking(p)) { /*Arriving on new processor*/
247             m->arr1=malloc(m->n1*sizeof(double));
248             m->arr2=malloc(m->n2*sizeof(int));
249           }
250           pup_doubles(p,m->arr1,m->n1);
251           pup_ints(p,m->arr2,m->n2);
252           if (pup_isDeleting(p)) { /*Leaving old processor*/
253             free(m->arr1);
254             free(m->arr2);
255           }
256         }
257
258 This single pup function can be used to copy the my_block data into a
259 message buffer and free the old heap storage (deleting pupper); allocate
260 storage on the new processor and copy the message data back (unpacking
261 pupper); or save the heap data for debugging or checkpointing.
262
263 A Fortran block TYPE and corresponding pup routine is as follows:
264
265 .. code-block:: fortran
266
267         MODULE my_block_mod
268           TYPE my_block
269             INTEGER :: n1,n2x,n2y
270             DOUBLE PRECISION, ALLOCATABLE, DIMENSION(:) :: arr1
271             INTEGER, ALLOCATABLE, DIMENSION(:,:) :: arr2
272           END TYPE
273         END MODULE
274
275         SUBROUTINE pup_my_block(p,m)
276           IMPLICIT NONE
277           USE my_block_mod
278           USE pupmod
279           INTEGER :: p
280           TYPE(my_block) :: m
281           IF (fpup_isUnpacking(p)) THEN
282             ALLOCATE(m%arr1(m%n1))
283             ALLOCATE(m%arr2(m%n2x,m%n2y))
284           END IF
285           call fpup_doubles(p,m%arr1,m%n1)
286           call fpup_ints(p,m%arr2,m%n2x*m%n2y)
287           IF (fpup_isDeleting(p)) THEN
288             DEALLOCATE(m%arr1)
289             DEALLOCATE(m%arr2)
290           END IF
291         END SUBROUTINE
292
293 You indicate to TCharm that you want a pup routine called using the
294 routine below. An arbitrary number of blocks can be registered in this
295 fashion.
296
297 .. code-block:: c++
298
299   void TCHARM_Register(void *block, TCharmPupFn pup_fn)
300
301 .. code-block:: fortran
302
303   SUBROUTINE TCHARM_Register(block,pup_fn)
304   TYPE(varies), POINTER :: block
305   SUBROUTINE :: pup_fn
306
307 Associate the given data block and pup function. Can only be called
308 from the parallel context. For the declarations above, you call
309 TCHARM_Register as:
310
311 .. code-block:: c++
312
313              /*In C/C++ driver() function*/
314              my_block m;
315              TCHARM_Register(m,(TCharmPupFn)pup_my_block);
316
317
318 .. code-block:: fortran
319
320              !- In Fortran driver subroutine
321              use my_block_mod
322              interface
323                subroutine pup_my_block(p,m)
324                  use my_block_mod
325                  INTEGER :: p
326                  TYPE(my_block) :: m
327                end subroutine
328              end interface
329              TYPE(my_block), TARGET :: m
330              call TCHARM_Register(m,pup_my_block)
331
332 Note that the data block must be allocated on the stack. Also, in
333 Fortran, the "TARGET" attribute must be used on the block (as above) or
334 else the compiler may not update values during a migration, because it
335 believes only it can access the block.
336
337 .. code-block:: c++
338
339   void TCHARM_Migrate()
340
341 .. code-block:: fortran
342
343   subroutine TCHARM_Migrate()
344
345 Informs the load balancing system that you are ready to be migrated, if
346 needed. If the system decides to migrate you, the pup function passed to
347 TCHARM_Register will first be called with a sizing pupper, then a
348 packing, deleting pupper. Your stack and pupped data will then be sent
349 to the destination machine, where your pup function will be called with
350 an unpacking pupper. TCHARM_Migrate will then return. Can only be called
351 from in the parallel context.
352
353 .. _sec_readonlyglobal:
354
355 Readonly Global Variables
356 -------------------------
357
358 You can also use a pup routine to set up initial values for global
359 variables on all processors. This pup routine is called with only a pup
360 handle, just after the serial setup routine, and just before any
361 parallel context routines start. The pup routine is never called with a
362 deleting pup handle, so you need not handle that case.
363
364 A C example is:
365
366 .. code-block:: c++
367
368         int g_arr[17];
369         double g_f;
370         int g_n; /*Length of array below*/
371         float *g_allocated; /*heap-allocated array*/
372
373         void pup_my_globals(pup_er p)
374         {
375           pup_ints(p,g_arr,17);
376           pup_double(p,&g_f);
377           pup_int(p,&g_n);
378           if (pup_isUnpacking(p)) { /*Arriving on new processor*/
379             g_allocated=malloc(g_n*sizeof(float));
380           }
381           pup_floats(p,g_allocated,g_n);
382         }
383
384 A Fortran example is:
385
386 .. code-block:: fortran
387
388         MODULE my_globals_mod
389           INTEGER :: g_arr(17)
390           DOUBLE PRECISION :: g_f
391           INTEGER :: g_n
392           SINGLE PRECISION, ALLOCATABLE :: g_allocated(:)
393         END MODULE
394
395         SUBROUTINE pup_my_globals(p)
396           IMPLICIT NONE
397           USE my_globals_mod
398           USE pupmod
399           INTEGER :: p
400           call fpup_ints(p,g_arr,17)
401           call fpup_double(p,g_f)
402           call fpup_int(p,g_n)
403           IF (fpup_isUnpacking(p)) THEN
404             ALLOCATE(g_allocated(g_n))
405           END IF
406           call fpup_floats(p,g_allocated,g_n)
407         END SUBROUTINE
408
409 You register your global variable pup routine using the method below.
410 Multiple pup routines can be registered the same way.
411
412 .. code-block:: c++
413
414   void TCHARM_Readonly_globals(TCharmPupGlobalFn pup_fn)
415
416 .. code-block:: fortran
417
418   SUBROUTINE TCHARM_Readonly_globals(pup_fn)
419   SUBROUTINE :: pup_fn
420
421 .. _sec:combining:
422
423 Combining Frameworks
424 ====================
425
426 This section describes how to combine multiple frameworks in a single
427 application. You might want to do this, for example, to use AMPI
428 communication inside a finite element method solver.
429
430 You specify how you want the frameworks to be combined by writing a
431 special setup routine that runs when the program starts. The setup
432 routine must be named TCHARM_User_setup. If you declare a user setup
433 routine, the standard framework setup routines (such as the FEM
434 framework’s init routine) are bypassed, and you do all the setup in the
435 user setup routine.
436
437 The setup routine creates a set of threads and then attaches frameworks
438 to the threads. Several different frameworks can be attached to one
439 thread set, and there can be several sets of threads; however, the most
440 frameworks cannot be attached more than once to single set of threads.
441 That is, a single thread cannot have two attached AMPI frameworks, since
442 the MPI_COMM_WORLD for such a thread would be indeterminate.
443
444 .. code-block:: c++
445
446   void TCHARM_Create(int nThreads, TCharmThreadStartFn thread_fn)
447
448 .. code-block:: fortran
449
450   SUBROUTINE TCHARM_Create(nThreads,thread_fn)
451   INTEGER, INTENT(in) :: nThreads
452   SUBROUTINE :: thread_fn
453
454 Create a new set of TCharm threads of the given size. The threads will
455 execute the given function, which is normally your user code. You
456 should call ``TCHARM_Get_num_chunks()`` to get the number of threads from
457 the command line. This routine can only be called from your
458 ``TCHARM_User_setup`` routine.
459
460 You then attach frameworks to the new threads. The order in which
461 frameworks are attached is irrelevant, but attach commands always apply
462 to the current set of threads.
463
464 To attach a chare array to the TCharm array, use:
465
466 .. code-block:: c++
467
468   CkArrayOptions TCHARM_Attach_start(CkArrayID *retTCharmArray,int
469   *retNumElts)
470
471 This function returns a CkArrayOptions object that will
472 bind your chare array to the TCharm array, in addition to returning the
473 TCharm array proxy and number of elements by reference. If you are using
474 frameworks like AMPI, they will automatically attach themselves to the
475 TCharm array in their initialization routines.
476
477 .. _sec:cla:
478
479 Command-line Options
480 ====================
481
482 The complete set of link-time arguments relevant to TCharm is:
483
484 -memory isomalloc
485   Enable memory allocation that will automatically migrate with the
486   thread, as described in
487   Section :numref:`sec:isomalloc`.
488
489 -balancer B
490   Enable this load balancing strategy. The current set of balancers B
491   includes RefineLB (make only small changes each time), MetisLB (remap
492   threads using graph partitioning library), HeapCentLB (remap threads
493   using a greedy algorithm), and RandCentLB (remap threads to random
494   processors). You can only have one balancer.
495
496 -module F
497   Link in this framework. The current set of frameworks F includes
498   ampi, collide, fem, mblock, and netfem. You can link in multiple
499   frameworks.
500
501 The complete set of command-line arguments relevant to TCharm is:
502
503 \+p N
504   Run on N physical processors.
505
506 +vp N
507   Create N “virtual processors”, or threads. This is the value returned
508   by TCharmGetNumChunks.
509
510 ++debug
511   Start each program in a debugger window. See Charm++ Installation and
512   Usage Manual for details.
513
514 +tcharm\_stacksize N
515   Create N-byte thread stacks. This value can be overridden using
516   TCharmSetStackSize().
517
518 +tcharm\_nomig
519   Disable thread migration. This can help determine whether a problem
520   you encounter is caused by our migration framework.
521
522 +tcharm\_nothread
523   Disable threads entirely. This can help determine whether a problem
524   you encounter is caused by our threading framework. This generally
525   only works properly when using only one thread.
526
527 +tcharm\_trace F
528   Trace all calls made to the framework F. This can help to understand
529   a complex program. This feature is not available if Charm++ was
530   compiled with CMK_OPTIMIZE.
531
532 .. _sec:tlib:
533
534 Writing a library using TCharm
535 ==============================
536
537 Until now, things were presented from the perspective of a user—one who
538 writes a program for a library written on TCharm. This section gives an
539 overview of how to go about writing a library in Charm++ that uses
540 TCharm.
541
542 - Compared to using plain MPI, TCharm provides the ability to access
543   all of Charm++, including arrays and groups.
544
545 - Compared to using plain Charm++, using TCharm with your library
546   automatically provides your users with a clean C/F90 API (described
547   in the preceding chapters) for basic thread memory management, I/O,
548   and migration. It also allows you to use a convenient
549   "thread->suspend()" and "thread->resume()" API for blocking a thread,
550   and works properly with the load balancer, unlike
551   CthSuspend/CthAwaken.
552
553 The overall scheme for writing a TCharm-based library "Foo" is:
554
555 #. You must provide a FOO_Init routine that creates anything you’ll
556    need, which normally includes a Chare Array of your own objects. The
557    user will call your FOO_Init routine from their main work routine;
558    and normally FOO_Init routines are collective.
559
560 #. In your FOO_Init routine, create your array bound it to the running
561    TCharm threads, by creating it using the CkArrayOptions returned by
562    TCHARM_Attach_start. Be sure to only create the array once, by
563    checking if you’re the master before creating the array.
564
565    One simple way to make the non-master threads block until the
566    corresponding local array element is created is to use TCharm
567    semaphores. These are simply a one-pointer slot you can assign using
568    TCharm::semaPut and read with TCharm::semaGet. They’re useful in this
569    context because a TCharm::semaGet blocks if a local TCharm::semaGet
570    hasn’t yet executed.
571
572    .. code-block:: c++
573
574       //This is either called by FooFallbackSetuo mentioned above, or by the user
575       //directly from TCHARM_User_setup (for multi-module programs)
576       void FOO_Init(void)
577       {
578         if (TCHARM_Element()==0) {
579           CkArrayID threadsAID; int nchunks;
580           CkArrayOptions opts=TCHARM_Attach_start(&threadsAID,&nchunks);
581
582         //actually create your library array here (FooChunk in this case)
583           CkArrayID aid = CProxy_FooChunk::ckNew(opt);
584         }
585         FooChunk *arr=(FooChunk *)TCharm::semaGet(FOO_TCHARM_SEMAID);
586       }
587
588 #. Depending on your library API, you may have to set up a
589    thread-private variable(Ctv) to point to your library object. This is
590    needed to regain context when you are called by the user. A better
591    design is to avoid the Ctv, and instead hand the user an opaque
592    handle that includes your array proxy.
593
594    .. code-block:: c++
595
596       //_fooptr is the Ctv that points to the current chunk FooChunk and is only valid in
597       //routines called from fooDriver()
598       CtvStaticDeclare(FooChunk *, _fooptr);
599
600       /* The following routine is listed as an initcall in the .ci file */
601       /*initcall*/ void fooNodeInit(void)
602       {
603         CtvInitialize(FooChunk*, _fooptr);
604       }
605
606 #. Define the array used by the library
607
608    .. code-block:: c++
609
610       class FooChunk: public TCharmClient1D {
611          CProxy_FooChunk thisProxy;
612       protected:
613          //called by TCharmClient1D when thread changes
614          virtual void setupThreadPrivate(CthThread forThread)
615          {
616             CtvAccessOther(forThread, _fooptr) = this;
617          }
618
619          FooChunk(CkArrayID aid):TCharmClient1D(aid)
620          {
621             thisProxy = this;
622             tCharmClientInit();
623             TCharm::semaPut(FOO_TCHARM_SEMAID,this);
624             //add any other initialization here
625          }
626
627          virtual void pup(PUP::er &p) {
628            TCharmClient1D::pup(p);
629            //usual pup calls
630          }
631
632          // ...any other calls you need...
633          int doCommunicate(...);
634          void recvReply(someReplyMsg *m);
635          ........
636       }
637
638 #. Block a thread for communication using thread->suspend and
639    thread->resume
640
641    .. code-block:: c++
642
643       int FooChunk::doCommunicate(...)
644       {
645          replyGoesHere = NULL;
646          thisProxy[destChunk].sendRequest(...);
647          thread->suspend(); //wait for reply to come back
648          return replyGoesHere->data;
649       }
650
651       void FooChunk::recvReply(someReplyMsg *m)
652       {
653         if(replyGoesHere!=NULL) CkAbort("FooChunk: unexpected reply\n");
654         replyGoesHere = m;
655         thread->resume(); //Got the reply -- start client again
656       }
657
658 #. Add API calls. This is how user code running in the thread interacts
659    with the newly created library. Calls to TCHARM_API_TRACE macro must
660    be added to the start of every user-callable method. In addition to
661    tracing, these disable isomalloc allocation.
662
663    The charm-api.h macros CLINKAGE, FLINKAGE and FTN_NAME should be used to
664    provide both C and FORTRAN versions of each API call. You should use
665    the "MPI capitalization standard", where the library name is all
666    caps, followed by a capitalized first word, with all subsequent words
667    lowercase, separated by underscores. This capitalization system is
668    consistent, and works well with case-insensitive languages like
669    Fortran.
670
671    Fortran parameter passing is a bit of an art, but basically for
672    simple types like int (INTEGER in fortran), float (SINGLE PRECISION
673    or REAL*4), and double (DOUBLE PRECISION or REAL*8), things work
674    well. Single parameters are always passed via pointer in Fortran, as
675    are arrays. Even though Fortran indexes arrays based at 1, it will
676    pass you a pointer to the first element, so you can use the regular C
677    indexing. The only time Fortran indexing need be considered is when
678    the user passes you an index-the int index will need to be
679    decremented before use, or incremented before a return.
680
681    .. code-block:: c++
682
683       CLINKAGE void FOO_Communicate(int x, double y, int * arr) {
684          TCHARM_API_TRACE("FOO_Communicate", "foo"); //2nd parameter is the name of the library
685          FooChunk *f = CtvAccess(_fooptr);
686          f->doCommunicate(x, y, arr);
687       }
688
689       //In fortran, everything is passed via pointers
690       FLINKAGE void FTN_NAME(FOO_COMMUNICATE, foo_communicate)
691            (int *x, double *y, int *arr)
692       {
693          TCHARM_API_TRACE("FOO_COMMUNICATE", "foo");
694          FooChunk *f = CtvAccess(_fooptr);
695          f->doCommunicate(*x, *y, arr);
696       }
697
698 .. [1]
699    The interface used is mmap.