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