1 /*
2  * kmp_threadprivate.c -- OpenMP threadprivate support library
3  */
4 
5 
6 //===----------------------------------------------------------------------===//
7 //
8 //                     The LLVM Compiler Infrastructure
9 //
10 // This file is dual licensed under the MIT and the University of Illinois Open
11 // Source Licenses. See LICENSE.txt for details.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 
16 #include "kmp.h"
17 #include "kmp_itt.h"
18 #include "kmp_i18n.h"
19 
20 /* ------------------------------------------------------------------------ */
21 /* ------------------------------------------------------------------------ */
22 
23 #define USE_CHECKS_COMMON
24 
25 #define KMP_INLINE_SUBR         1
26 
27 
28 /* ------------------------------------------------------------------------ */
29 /* ------------------------------------------------------------------------ */
30 
31 void
32 kmp_threadprivate_insert_private_data( int gtid, void *pc_addr, void *data_addr, size_t pc_size );
33 struct private_common *
34 kmp_threadprivate_insert( int gtid, void *pc_addr, void *data_addr, size_t pc_size );
35 
36 struct shared_table     __kmp_threadprivate_d_table;
37 
38 /* ------------------------------------------------------------------------ */
39 /* ------------------------------------------------------------------------ */
40 
41 static
42 #ifdef KMP_INLINE_SUBR
43 __forceinline
44 #endif
45 struct private_common *
46 __kmp_threadprivate_find_task_common( struct common_table *tbl, int gtid, void *pc_addr )
47 
48 {
49     struct private_common *tn;
50 
51 #ifdef KMP_TASK_COMMON_DEBUG
52     KC_TRACE( 10, ( "__kmp_threadprivate_find_task_common: thread#%d, called with address %p\n",
53                     gtid, pc_addr ) );
54     dump_list();
55 #endif
56 
57     for (tn = tbl->data[ KMP_HASH(pc_addr) ]; tn; tn = tn->next) {
58         if (tn->gbl_addr == pc_addr) {
59 #ifdef KMP_TASK_COMMON_DEBUG
60             KC_TRACE( 10, ( "__kmp_threadprivate_find_task_common: thread#%d, found node %p on list\n",
61                             gtid, pc_addr ) );
62 #endif
63             return tn;
64         }
65     }
66     return 0;
67 }
68 
69 static
70 #ifdef KMP_INLINE_SUBR
71 __forceinline
72 #endif
73 struct shared_common *
74 __kmp_find_shared_task_common( struct shared_table *tbl, int gtid, void *pc_addr )
75 {
76     struct shared_common *tn;
77 
78     for (tn = tbl->data[ KMP_HASH(pc_addr) ]; tn; tn = tn->next) {
79         if (tn->gbl_addr == pc_addr) {
80 #ifdef KMP_TASK_COMMON_DEBUG
81             KC_TRACE( 10, ( "__kmp_find_shared_task_common: thread#%d, found node %p on list\n",
82                             gtid, pc_addr ) );
83 #endif
84             return tn;
85         }
86     }
87     return 0;
88 }
89 
90 
91 /*
92  *      Create a template for the data initialized storage.
93  *      Either the template is NULL indicating zero fill,
94  *      or the template is a copy of the original data.
95  */
96 
97 static struct private_data *
98 __kmp_init_common_data( void *pc_addr, size_t pc_size )
99 {
100     struct private_data *d;
101     size_t       i;
102     char        *p;
103 
104     d = (struct private_data *) __kmp_allocate( sizeof( struct private_data ) );
105 /*
106     d->data = 0;  // AC: commented out because __kmp_allocate zeroes the memory
107     d->next = 0;
108 */
109     d->size = pc_size;
110     d->more = 1;
111 
112     p = (char*)pc_addr;
113 
114     for (i = pc_size;  i > 0; --i) {
115         if (*p++ != '\0') {
116             d->data = __kmp_allocate( pc_size );
117             KMP_MEMCPY( d->data, pc_addr, pc_size );
118             break;
119         }
120     }
121 
122     return d;
123 }
124 
125 /*
126  *      Initialize the data area from the template.
127  */
128 
129 static void
130 __kmp_copy_common_data( void *pc_addr, struct private_data *d )
131 {
132     char *addr = (char *) pc_addr;
133     int   i, offset;
134 
135     for (offset = 0; d != 0; d = d->next) {
136         for (i = d->more; i > 0; --i) {
137             if (d->data == 0)
138                 memset( & addr[ offset ], '\0', d->size );
139             else
140                 KMP_MEMCPY( & addr[ offset ], d->data, d->size );
141             offset += d->size;
142         }
143     }
144 }
145 
146 /* ------------------------------------------------------------------------ */
147 /* ------------------------------------------------------------------------ */
148 
149 /* we are called from __kmp_serial_initialize() with __kmp_initz_lock held. */
150 void
151 __kmp_common_initialize( void )
152 {
153     if( ! TCR_4(__kmp_init_common) ) {
154         int q;
155 #ifdef KMP_DEBUG
156         int gtid;
157 #endif
158 
159         __kmp_threadpriv_cache_list = NULL;
160 
161 #ifdef KMP_DEBUG
162         /* verify the uber masters were initialized */
163         for(gtid = 0 ; gtid < __kmp_threads_capacity; gtid++ )
164             if( __kmp_root[gtid] ) {
165                 KMP_DEBUG_ASSERT( __kmp_root[gtid]->r.r_uber_thread );
166                 for ( q = 0; q< KMP_HASH_TABLE_SIZE; ++q)
167                     KMP_DEBUG_ASSERT( !__kmp_root[gtid]->r.r_uber_thread->th.th_pri_common->data[q] );
168 /*                    __kmp_root[ gitd ]-> r.r_uber_thread -> th.th_pri_common -> data[ q ] = 0;*/
169             }
170 #endif /* KMP_DEBUG */
171 
172         for (q = 0; q < KMP_HASH_TABLE_SIZE; ++q)
173             __kmp_threadprivate_d_table.data[ q ] = 0;
174 
175         TCW_4(__kmp_init_common, TRUE);
176     }
177 }
178 
179 /* Call all destructors for threadprivate data belonging to all threads.
180    Currently unused! */
181 void
182 __kmp_common_destroy( void )
183 {
184     if( TCR_4(__kmp_init_common) ) {
185         int q;
186 
187         TCW_4(__kmp_init_common, FALSE);
188 
189         for (q = 0; q < KMP_HASH_TABLE_SIZE; ++q) {
190             int gtid;
191             struct private_common *tn;
192             struct shared_common  *d_tn;
193 
194             /*  C++ destructors need to be called once per thread before exiting  */
195             /*  don't call destructors for master thread though unless we used copy constructor */
196 
197             for (d_tn = __kmp_threadprivate_d_table.data[ q ]; d_tn; d_tn = d_tn->next) {
198                 if (d_tn->is_vec) {
199                     if (d_tn->dt.dtorv != 0) {
200                         for (gtid = 0; gtid < __kmp_all_nth; ++gtid) {
201                             if( __kmp_threads[gtid] ) {
202                                 if( (__kmp_foreign_tp) ? (! KMP_INITIAL_GTID (gtid)) :
203                                                          (! KMP_UBER_GTID (gtid)) ) {
204                                     tn = __kmp_threadprivate_find_task_common( __kmp_threads[ gtid ]->th.th_pri_common,
205                                                                                gtid, d_tn->gbl_addr );
206                                     if (tn) {
207                                         (*d_tn->dt.dtorv) (tn->par_addr, d_tn->vec_len);
208                                     }
209                                 }
210                             }
211                         }
212                         if (d_tn->obj_init != 0) {
213                             (*d_tn->dt.dtorv) (d_tn->obj_init, d_tn->vec_len);
214                         }
215                     }
216                 } else {
217                     if (d_tn->dt.dtor != 0) {
218                         for (gtid = 0; gtid < __kmp_all_nth; ++gtid) {
219                             if( __kmp_threads[gtid] ) {
220                                 if( (__kmp_foreign_tp) ? (! KMP_INITIAL_GTID (gtid)) :
221                                                          (! KMP_UBER_GTID (gtid)) ) {
222                                     tn = __kmp_threadprivate_find_task_common( __kmp_threads[ gtid ]->th.th_pri_common,
223                                                                                gtid, d_tn->gbl_addr );
224                                     if (tn) {
225                                         (*d_tn->dt.dtor) (tn->par_addr);
226                                     }
227                                 }
228                             }
229                         }
230                         if (d_tn->obj_init != 0) {
231                             (*d_tn->dt.dtor) (d_tn->obj_init);
232                         }
233                     }
234                 }
235             }
236             __kmp_threadprivate_d_table.data[ q ] = 0;
237         }
238     }
239 }
240 
241 /* Call all destructors for threadprivate data belonging to this thread */
242 void
243 __kmp_common_destroy_gtid( int gtid )
244 {
245     struct private_common *tn;
246     struct shared_common *d_tn;
247 
248     KC_TRACE( 10, ("__kmp_common_destroy_gtid: T#%d called\n", gtid ) );
249     if( (__kmp_foreign_tp) ? (! KMP_INITIAL_GTID (gtid)) :
250                              (! KMP_UBER_GTID (gtid)) ) {
251 
252         if( TCR_4(__kmp_init_common) ) {
253 
254             /* Cannot do this here since not all threads have destroyed their data */
255             /* TCW_4(__kmp_init_common, FALSE); */
256 
257             for (tn = __kmp_threads[ gtid ]->th.th_pri_head; tn; tn = tn->link) {
258 
259                 d_tn = __kmp_find_shared_task_common( &__kmp_threadprivate_d_table,
260                                                       gtid, tn->gbl_addr );
261 
262                 KMP_DEBUG_ASSERT( d_tn );
263 
264                 if (d_tn->is_vec) {
265                     if (d_tn->dt.dtorv != 0) {
266                         (void) (*d_tn->dt.dtorv) (tn->par_addr, d_tn->vec_len);
267                     }
268                     if (d_tn->obj_init != 0) {
269                         (void) (*d_tn->dt.dtorv) (d_tn->obj_init, d_tn->vec_len);
270                     }
271                 } else {
272                     if (d_tn->dt.dtor != 0) {
273                         (void) (*d_tn->dt.dtor) (tn->par_addr);
274                     }
275                     if (d_tn->obj_init != 0) {
276                         (void) (*d_tn->dt.dtor) (d_tn->obj_init);
277                     }
278                 }
279             }
280             KC_TRACE( 30, ("__kmp_common_destroy_gtid: T#%d threadprivate destructors complete\n",
281                            gtid ) );
282         }
283     }
284 }
285 
286 /* ------------------------------------------------------------------------ */
287 /* ------------------------------------------------------------------------ */
288 
289 #ifdef KMP_TASK_COMMON_DEBUG
290 static void
291 dump_list( void )
292 {
293     int p, q;
294 
295     for (p = 0; p < __kmp_all_nth; ++p) {
296         if( !__kmp_threads[p] ) continue;
297         for (q = 0; q < KMP_HASH_TABLE_SIZE; ++q) {
298             if (__kmp_threads[ p ]->th.th_pri_common->data[ q ]) {
299                 struct private_common *tn;
300 
301                 KC_TRACE( 10, ( "\tdump_list: gtid:%d addresses\n", p ) );
302 
303                 for (tn = __kmp_threads[ p ]->th.th_pri_common->data[ q ]; tn; tn = tn->next)                 {
304                     KC_TRACE( 10, ( "\tdump_list: THREADPRIVATE: Serial %p -> Parallel %p\n",
305                                     tn->gbl_addr, tn->par_addr ) );
306                 }
307             }
308         }
309     }
310 }
311 #endif /* KMP_TASK_COMMON_DEBUG */
312 
313 
314 /*
315  * NOTE: this routine is to be called only from the serial part of the program.
316  */
317 
318 void
319 kmp_threadprivate_insert_private_data( int gtid, void *pc_addr, void *data_addr, size_t pc_size )
320 {
321     struct shared_common **lnk_tn, *d_tn;
322     KMP_DEBUG_ASSERT( __kmp_threads[ gtid ] &&
323             __kmp_threads[ gtid ] -> th.th_root -> r.r_active == 0 );
324 
325     d_tn = __kmp_find_shared_task_common( &__kmp_threadprivate_d_table,
326                                           gtid, pc_addr );
327 
328     if (d_tn == 0) {
329         d_tn = (struct shared_common *) __kmp_allocate( sizeof( struct shared_common ) );
330 
331         d_tn->gbl_addr = pc_addr;
332         d_tn->pod_init = __kmp_init_common_data( data_addr, pc_size );
333 /*
334         d_tn->obj_init = 0;  // AC: commented out because __kmp_allocate zeroes the memory
335         d_tn->ct.ctor = 0;
336         d_tn->cct.cctor = 0;;
337         d_tn->dt.dtor = 0;
338         d_tn->is_vec = FALSE;
339         d_tn->vec_len = 0L;
340 */
341         d_tn->cmn_size = pc_size;
342 
343         __kmp_acquire_lock( &__kmp_global_lock, gtid );
344 
345         lnk_tn = &(__kmp_threadprivate_d_table.data[ KMP_HASH(pc_addr) ]);
346 
347         d_tn->next = *lnk_tn;
348         *lnk_tn = d_tn;
349 
350         __kmp_release_lock( &__kmp_global_lock, gtid );
351     }
352 }
353 
354 struct private_common *
355 kmp_threadprivate_insert( int gtid, void *pc_addr, void *data_addr, size_t pc_size )
356 {
357     struct private_common *tn, **tt;
358     struct shared_common  *d_tn;
359 
360     /* +++++++++ START OF CRITICAL SECTION +++++++++ */
361 
362     __kmp_acquire_lock( & __kmp_global_lock, gtid );
363 
364     tn = (struct private_common *) __kmp_allocate( sizeof (struct private_common) );
365 
366     tn->gbl_addr = pc_addr;
367 
368     d_tn = __kmp_find_shared_task_common( &__kmp_threadprivate_d_table,
369                                           gtid, pc_addr );     /* Only the MASTER data table exists. */
370 
371     if (d_tn != 0) {
372         /* This threadprivate variable has already been seen. */
373 
374         if ( d_tn->pod_init == 0 && d_tn->obj_init == 0 ) {
375             d_tn->cmn_size = pc_size;
376 
377             if (d_tn->is_vec) {
378                 if (d_tn->ct.ctorv != 0) {
379                     /* Construct from scratch so no prototype exists */
380                     d_tn->obj_init = 0;
381                 }
382                 else if (d_tn->cct.cctorv != 0) {
383                     /* Now data initialize the prototype since it was previously registered */
384                     d_tn->obj_init = (void *) __kmp_allocate( d_tn->cmn_size );
385                     (void) (*d_tn->cct.cctorv) (d_tn->obj_init, pc_addr, d_tn->vec_len);
386                 }
387                 else {
388                     d_tn->pod_init = __kmp_init_common_data( data_addr, d_tn->cmn_size );
389                 }
390             } else {
391                 if (d_tn->ct.ctor != 0) {
392                     /* Construct from scratch so no prototype exists */
393                     d_tn->obj_init = 0;
394                 }
395                 else if (d_tn->cct.cctor != 0) {
396                     /* Now data initialize the prototype since it was previously registered */
397                     d_tn->obj_init = (void *) __kmp_allocate( d_tn->cmn_size );
398                     (void) (*d_tn->cct.cctor) (d_tn->obj_init, pc_addr);
399                 }
400                 else {
401                     d_tn->pod_init = __kmp_init_common_data( data_addr, d_tn->cmn_size );
402                 }
403             }
404         }
405     }
406     else {
407         struct shared_common **lnk_tn;
408 
409         d_tn = (struct shared_common *) __kmp_allocate( sizeof( struct shared_common ) );
410         d_tn->gbl_addr = pc_addr;
411         d_tn->cmn_size = pc_size;
412         d_tn->pod_init = __kmp_init_common_data( data_addr, pc_size );
413 /*
414         d_tn->obj_init = 0;  // AC: commented out because __kmp_allocate zeroes the memory
415         d_tn->ct.ctor = 0;
416         d_tn->cct.cctor = 0;
417         d_tn->dt.dtor = 0;
418         d_tn->is_vec = FALSE;
419         d_tn->vec_len = 0L;
420 */
421         lnk_tn = &(__kmp_threadprivate_d_table.data[ KMP_HASH(pc_addr) ]);
422 
423         d_tn->next = *lnk_tn;
424         *lnk_tn = d_tn;
425     }
426 
427     tn->cmn_size = d_tn->cmn_size;
428 
429     if ( (__kmp_foreign_tp) ? (KMP_INITIAL_GTID (gtid)) : (KMP_UBER_GTID (gtid)) ) {
430         tn->par_addr = (void *) pc_addr;
431     }
432     else {
433         tn->par_addr = (void *) __kmp_allocate( tn->cmn_size );
434     }
435 
436     __kmp_release_lock( & __kmp_global_lock, gtid );
437 
438     /* +++++++++ END OF CRITICAL SECTION +++++++++ */
439 
440 #ifdef USE_CHECKS_COMMON
441         if (pc_size > d_tn->cmn_size) {
442             KC_TRACE( 10, ( "__kmp_threadprivate_insert: THREADPRIVATE: %p (%"
443                             KMP_UINTPTR_SPEC " ,%" KMP_UINTPTR_SPEC ")\n",
444                             pc_addr, pc_size, d_tn->cmn_size ) );
445             KMP_FATAL( TPCommonBlocksInconsist );
446         }
447 #endif /* USE_CHECKS_COMMON */
448 
449     tt = &(__kmp_threads[ gtid ]->th.th_pri_common->data[ KMP_HASH(pc_addr) ]);
450 
451 #ifdef KMP_TASK_COMMON_DEBUG
452     if (*tt != 0) {
453         KC_TRACE( 10, ( "__kmp_threadprivate_insert: WARNING! thread#%d: collision on %p\n",
454                         gtid, pc_addr ) );
455     }
456 #endif
457     tn->next = *tt;
458     *tt = tn;
459 
460 #ifdef KMP_TASK_COMMON_DEBUG
461     KC_TRACE( 10, ( "__kmp_threadprivate_insert: thread#%d, inserted node %p on list\n",
462                     gtid, pc_addr ) );
463     dump_list( );
464 #endif
465 
466     /* Link the node into a simple list */
467 
468     tn->link = __kmp_threads[ gtid ]->th.th_pri_head;
469     __kmp_threads[ gtid ]->th.th_pri_head = tn;
470 
471 #ifdef BUILD_TV
472     __kmp_tv_threadprivate_store( __kmp_threads[ gtid ], tn->gbl_addr, tn->par_addr );
473 #endif
474 
475     if( (__kmp_foreign_tp) ? (KMP_INITIAL_GTID (gtid)) : (KMP_UBER_GTID (gtid)) )
476         return tn;
477 
478     /*
479      * if C++ object with copy constructor, use it;
480      * else if C++ object with constructor, use it for the non-master copies only;
481      * else use pod_init and memcpy
482      *
483      * C++ constructors need to be called once for each non-master thread on allocate
484      * C++ copy constructors need to be called once for each thread on allocate
485      */
486 
487     /*
488      * C++ object with constructors/destructors;
489      * don't call constructors for master thread though
490      */
491     if (d_tn->is_vec) {
492         if ( d_tn->ct.ctorv != 0) {
493             (void) (*d_tn->ct.ctorv) (tn->par_addr, d_tn->vec_len);
494         } else if (d_tn->cct.cctorv != 0) {
495             (void) (*d_tn->cct.cctorv) (tn->par_addr, d_tn->obj_init, d_tn->vec_len);
496         } else if (tn->par_addr != tn->gbl_addr) {
497             __kmp_copy_common_data( tn->par_addr, d_tn->pod_init );
498         }
499     } else {
500         if ( d_tn->ct.ctor != 0 ) {
501             (void) (*d_tn->ct.ctor) (tn->par_addr);
502         } else if (d_tn->cct.cctor != 0) {
503             (void) (*d_tn->cct.cctor) (tn->par_addr, d_tn->obj_init);
504         } else if (tn->par_addr != tn->gbl_addr) {
505             __kmp_copy_common_data( tn->par_addr, d_tn->pod_init );
506         }
507     }
508 /* !BUILD_OPENMP_C
509     if (tn->par_addr != tn->gbl_addr)
510         __kmp_copy_common_data( tn->par_addr, d_tn->pod_init ); */
511 
512     return tn;
513 }
514 
515 /* ------------------------------------------------------------------------ */
516 /* We are currently parallel, and we know the thread id.                    */
517 /* ------------------------------------------------------------------------ */
518 
519 /*!
520  @ingroup THREADPRIVATE
521 
522  @param loc source location information
523  @param data  pointer to data being privatized
524  @param ctor  pointer to constructor function for data
525  @param cctor  pointer to copy constructor function for data
526  @param dtor  pointer to destructor function for data
527 
528  Register constructors and destructors for thread private data.
529  This function is called when executing in parallel, when we know the thread id.
530 */
531 void
532 __kmpc_threadprivate_register(ident_t *loc, void *data, kmpc_ctor ctor, kmpc_cctor cctor, kmpc_dtor dtor)
533 {
534     struct shared_common *d_tn, **lnk_tn;
535 
536     KC_TRACE( 10, ("__kmpc_threadprivate_register: called\n" ) );
537 
538 #ifdef USE_CHECKS_COMMON
539     /* copy constructor must be zero for current code gen (Nov 2002 - jph) */
540     KMP_ASSERT( cctor == 0);
541 #endif /* USE_CHECKS_COMMON */
542 
543     /* Only the global data table exists. */
544     d_tn = __kmp_find_shared_task_common( &__kmp_threadprivate_d_table, -1, data );
545 
546     if (d_tn == 0) {
547         d_tn = (struct shared_common *) __kmp_allocate( sizeof( struct shared_common ) );
548         d_tn->gbl_addr = data;
549 
550         d_tn->ct.ctor = ctor;
551         d_tn->cct.cctor = cctor;
552         d_tn->dt.dtor = dtor;
553 /*
554         d_tn->is_vec = FALSE;  // AC: commented out because __kmp_allocate zeroes the memory
555         d_tn->vec_len = 0L;
556         d_tn->obj_init = 0;
557         d_tn->pod_init = 0;
558 */
559         lnk_tn = &(__kmp_threadprivate_d_table.data[ KMP_HASH(data) ]);
560 
561         d_tn->next = *lnk_tn;
562         *lnk_tn = d_tn;
563     }
564 }
565 
566 void *
567 __kmpc_threadprivate(ident_t *loc, kmp_int32 global_tid, void *data, size_t size)
568 {
569     void *ret;
570     struct private_common *tn;
571 
572     KC_TRACE( 10, ("__kmpc_threadprivate: T#%d called\n", global_tid ) );
573 
574 #ifdef USE_CHECKS_COMMON
575     if (! __kmp_init_serial)
576         KMP_FATAL( RTLNotInitialized );
577 #endif /* USE_CHECKS_COMMON */
578 
579     if ( ! __kmp_threads[global_tid] -> th.th_root -> r.r_active && ! __kmp_foreign_tp ) {
580         /* The parallel address will NEVER overlap with the data_address */
581         /* dkp: 3rd arg to kmp_threadprivate_insert_private_data() is the data_address; use data_address = data */
582 
583         KC_TRACE( 20, ("__kmpc_threadprivate: T#%d inserting private data\n", global_tid ) );
584         kmp_threadprivate_insert_private_data( global_tid, data, data, size );
585 
586         ret = data;
587     }
588     else {
589         KC_TRACE( 50, ("__kmpc_threadprivate: T#%d try to find private data at address %p\n",
590                        global_tid, data ) );
591         tn = __kmp_threadprivate_find_task_common( __kmp_threads[ global_tid ]->th.th_pri_common, global_tid, data );
592 
593         if ( tn ) {
594             KC_TRACE( 20, ("__kmpc_threadprivate: T#%d found data\n", global_tid ) );
595 #ifdef USE_CHECKS_COMMON
596             if ((size_t) size > tn->cmn_size) {
597                 KC_TRACE( 10, ( "THREADPRIVATE: %p (%" KMP_UINTPTR_SPEC " ,%" KMP_UINTPTR_SPEC ")\n",
598                                 data, size, tn->cmn_size ) );
599                 KMP_FATAL( TPCommonBlocksInconsist );
600             }
601 #endif /* USE_CHECKS_COMMON */
602         }
603         else {
604             /* The parallel address will NEVER overlap with the data_address */
605             /* dkp: 3rd arg to kmp_threadprivate_insert() is the data_address; use data_address = data */
606             KC_TRACE( 20, ("__kmpc_threadprivate: T#%d inserting data\n", global_tid ) );
607             tn = kmp_threadprivate_insert( global_tid, data, data, size );
608         }
609 
610         ret = tn->par_addr;
611     }
612     KC_TRACE( 10, ("__kmpc_threadprivate: T#%d exiting; return value = %p\n",
613                    global_tid, ret ) );
614 
615     return ret;
616 }
617 
618 /*!
619  @ingroup THREADPRIVATE
620  @param loc source location information
621  @param global_tid  global thread number
622  @param data  pointer to data to privatize
623  @param size  size of data to privatize
624  @param cache  pointer to cache
625  @return pointer to private storage
626 
627  Allocate private storage for threadprivate data.
628 */
629 void *
630 __kmpc_threadprivate_cached(
631     ident_t *  loc,
632     kmp_int32  global_tid,   // gtid.
633     void *     data,         // Pointer to original global variable.
634     size_t     size,         // Size of original global variable.
635     void ***   cache
636 ) {
637     KC_TRACE( 10, ("__kmpc_threadprivate_cached: T#%d called with cache: %p, address: %p, size: %"
638                    KMP_SIZE_T_SPEC "\n",
639                    global_tid, *cache, data, size ) );
640 
641     if ( TCR_PTR(*cache) == 0) {
642         __kmp_acquire_lock( & __kmp_global_lock, global_tid );
643 
644         if ( TCR_PTR(*cache) == 0) {
645             __kmp_acquire_bootstrap_lock(&__kmp_tp_cached_lock);
646             __kmp_tp_cached = 1;
647             __kmp_release_bootstrap_lock(&__kmp_tp_cached_lock);
648             void ** my_cache;
649             KMP_ITT_IGNORE(
650             my_cache = (void**)
651                 __kmp_allocate(sizeof( void * ) * __kmp_tp_capacity + sizeof ( kmp_cached_addr_t ));
652                            );
653             // No need to zero the allocated memory; __kmp_allocate does that.
654             KC_TRACE( 50, ("__kmpc_threadprivate_cached: T#%d allocated cache at address %p\n",
655                            global_tid, my_cache ) );
656 
657             /* TODO: free all this memory in __kmp_common_destroy using __kmp_threadpriv_cache_list */
658             /* Add address of mycache to linked list for cleanup later  */
659             kmp_cached_addr_t *tp_cache_addr;
660 
661             tp_cache_addr = (kmp_cached_addr_t *) & my_cache[__kmp_tp_capacity];
662             tp_cache_addr -> addr = my_cache;
663             tp_cache_addr -> next = __kmp_threadpriv_cache_list;
664             __kmp_threadpriv_cache_list = tp_cache_addr;
665 
666             KMP_MB();
667 
668             TCW_PTR( *cache, my_cache);
669 
670             KMP_MB();
671         }
672 
673         __kmp_release_lock( & __kmp_global_lock, global_tid );
674     }
675 
676     void *ret;
677     if ((ret = TCR_PTR((*cache)[ global_tid ])) == 0) {
678         ret = __kmpc_threadprivate( loc, global_tid, data, (size_t) size);
679 
680         TCW_PTR( (*cache)[ global_tid ], ret);
681     }
682     KC_TRACE( 10, ("__kmpc_threadprivate_cached: T#%d exiting; return value = %p\n",
683                    global_tid, ret ) );
684 
685     return ret;
686 }
687 
688 /*!
689  @ingroup THREADPRIVATE
690  @param loc source location information
691  @param data  pointer to data being privatized
692  @param ctor  pointer to constructor function for data
693  @param cctor  pointer to copy constructor function for data
694  @param dtor  pointer to destructor function for data
695  @param vector_length length of the vector (bytes or elements?)
696  Register vector constructors and destructors for thread private data.
697 */
698 void
699 __kmpc_threadprivate_register_vec( ident_t *loc, void *data, kmpc_ctor_vec ctor,
700                                    kmpc_cctor_vec cctor, kmpc_dtor_vec dtor,
701                                    size_t vector_length )
702 {
703     struct shared_common *d_tn, **lnk_tn;
704 
705     KC_TRACE( 10, ("__kmpc_threadprivate_register_vec: called\n" ) );
706 
707 #ifdef USE_CHECKS_COMMON
708     /* copy constructor must be zero for current code gen (Nov 2002 - jph) */
709     KMP_ASSERT( cctor == 0);
710 #endif /* USE_CHECKS_COMMON */
711 
712     d_tn = __kmp_find_shared_task_common( &__kmp_threadprivate_d_table,
713                                           -1, data );        /* Only the global data table exists. */
714 
715     if (d_tn == 0) {
716         d_tn = (struct shared_common *) __kmp_allocate( sizeof( struct shared_common ) );
717         d_tn->gbl_addr = data;
718 
719         d_tn->ct.ctorv = ctor;
720         d_tn->cct.cctorv = cctor;
721         d_tn->dt.dtorv = dtor;
722         d_tn->is_vec = TRUE;
723         d_tn->vec_len = (size_t) vector_length;
724 /*
725         d_tn->obj_init = 0;  // AC: commented out because __kmp_allocate zeroes the memory
726         d_tn->pod_init = 0;
727 */
728         lnk_tn = &(__kmp_threadprivate_d_table.data[ KMP_HASH(data) ]);
729 
730         d_tn->next = *lnk_tn;
731         *lnk_tn = d_tn;
732     }
733 }
734