File Coverage

lib/PDL/Core/pdlmagic.c
Criterion Covered Total %
statement 144 239 60.2
branch 57 130 43.8
condition n/a
subroutine n/a
pod n/a
total 201 369 54.4


line stmt bran cond sub pod time code
1             #include "pdlcore.h"
2              
3             /* Variable storing the pthread ID for the main PDL thread.
4             * This is used to tell if we are in the main pthread, or in one of
5             * the pthreads spawned for PDL processing
6             * This is only used when compiled with pthreads.
7             */
8             #ifdef PDL_PTHREAD
9             static pthread_t pdl_main_pthreadID;
10             static int done_pdl_main_pthreadID_init = 0;
11              
12             /* deferred error messages are stored here. We can only barf/warn from the main
13             * thread, so worker threads complain here and the complaints are printed out
14             * altogether later
15             */
16             static char* pdl_pthread_barf_msgs = NULL;
17             static size_t pdl_pthread_barf_msgs_len = 0;
18             static char* pdl_pthread_warn_msgs = NULL;
19             static size_t pdl_pthread_warn_msgs_len = 0;
20              
21             #endif
22              
23              
24             /* Singly linked list */
25             /* Note that this zeroes ->next!) */
26              
27 90           void pdl__magic_add(pdl *it,pdl_magic *mag)
28             {
29 90           pdl_magic **foo = (pdl_magic **)(&(it->magic));
30 90 50         while(*foo) {
31 0           foo = &((*foo)->next);
32             }
33 90           (*foo) = mag;
34 90           mag->next = NULL;
35 90           }
36              
37 25           pdl_error pdl__magic_rm(pdl *it,pdl_magic *mag)
38             {
39 25           pdl_error PDL_err = {0, NULL, 0};
40 25           pdl_magic **foo = (pdl_magic **)(&(it->magic));
41 25           int found = 0;
42 50 100         while(*foo) {
43 25 50         if(*foo == mag) {
44 25           *foo = (*foo)->next;
45 25           found = 1;
46             }
47             else{
48 0           foo = &((*foo)->next);
49             }
50             }
51 25 50         if( !found ){
52 0           return pdl_make_error_simple(PDL_EUSERERROR, "PDL:Magic not found: Internal error\n");
53             }
54 25           return PDL_err;
55             }
56              
57 65           void pdl__magic_free(pdl *it)
58             {
59 65 50         if (pdl__ismagic(it) && !pdl__magic_isundestroyable(it)) {
    50          
60 65           pdl_magic *foo = (pdl_magic *)(it->magic);
61 130 100         while(foo) {
62 65           pdl_magic *next = foo->next;
63 65           free(foo);
64 65           foo = next;
65             }
66             }
67 65           }
68              
69             /* Test for undestroyability */
70              
71 116522           int pdl__magic_isundestroyable(pdl *it)
72             {
73 116522           pdl_magic **foo = (pdl_magic **)(&(it->magic));
74 116652 100         while(*foo) {
75 130 50         if((*foo)->what & PDL_MAGIC_UNDESTROYABLE) {return 1;}
76 130           foo = &((*foo)->next);
77             }
78 116522           return 0;
79             }
80              
81             /* Call magics */
82              
83 66           void *pdl__call_magic(pdl *it,int which)
84             {
85 66           void *ret = NULL;
86 66           pdl_magic **foo = (pdl_magic **)(&(it->magic));
87 132 100         while(*foo) {
88 66 50         if((*foo)->what & which) {
89 0 0         if((*foo)->what & PDL_MAGIC_DELAYED)
90 0           pdl_add_delayed_magic(*foo);
91             else
92 0           ret = (*foo)->vtable->cast(*foo);
93             /* Cast spell */
94             }
95 66           foo = &((*foo)->next);
96             }
97 66           return ret;
98             }
99              
100             /* XXX FINDS ONLY FIRST */
101 3347           pdl_magic *pdl__find_magic(pdl *it, int which)
102             {
103 3347           pdl_magic **foo = (pdl_magic **)(&(it->magic));
104 3347 100         while(*foo) {
105 3346 50         if((*foo)->what & which) {
106 3346           return *foo;
107             }
108 0           foo = &((*foo)->next);
109             }
110 1           return NULL;
111             }
112              
113 0           pdl_magic *pdl__print_magic(pdl *it)
114             {
115 0           pdl_magic **foo = (pdl_magic **)(&(it->magic));
116 0 0         while(*foo) {
117 0           printf("Magic %p\ttype: ",*foo);
118 0 0         if((*foo)->what & PDL_MAGIC_MARKCHANGED)
119 0           printf("PDL_MAGIC_MARKCHANGED");
120 0 0         else if ((*foo)->what & PDL_MAGIC_THREADING)
121 0           printf("PDL_MAGIC_THREADING");
122             else
123 0           printf("UNKNOWN");
124 0 0         if ((*foo)->what & (PDL_MAGIC_DELAYED|PDL_MAGIC_UNDESTROYABLE))
125             {
126 0           printf(" qualifier(s): ");
127 0 0         if ((*foo)->what & PDL_MAGIC_DELAYED)
128 0           printf(" PDL_MAGIC_DELAYED");
129 0 0         if ((*foo)->what & PDL_MAGIC_UNDESTROYABLE)
130 0           printf(" PDL_MAGIC_UNDESTROYABLE");
131             }
132 0           printf("\n");
133 0           foo = &((*foo)->next);
134             }
135 0           return NULL;
136             }
137              
138              
139 112751           int pdl__ismagic(pdl *it)
140             {
141 112751           return (it->magic != 0);
142             }
143              
144             static pdl_magic **delayed=NULL;
145             static PDL_Indx ndelayed = 0;
146 0           void pdl_add_delayed_magic(pdl_magic *mag) {
147             /* FIXME: Common realloc mistake: 'delayed' nulled but not freed upon failure */
148 0           delayed = realloc(delayed,sizeof(*delayed)*++ndelayed);
149 0           delayed[ndelayed-1] = mag;
150 0           }
151 0           void pdl_run_delayed_magic(void) {
152             PDL_Indx i;
153 0           pdl_magic **oldd = delayed; /* In case someone makes new delayed stuff */
154 0           PDL_Indx nold = ndelayed;
155 0           delayed = NULL;
156 0           ndelayed = 0;
157 0 0         for(i=0; i
158 0           oldd[i]->vtable->cast(oldd[i]);
159             }
160 0           free(oldd);
161 0           }
162              
163             /****************
164             *
165             * ->bind - magic
166             */
167 0           void *svmagic_cast(pdl_magic *mag)
168             {
169 0           pdl_magic_perlfunc *magp = (pdl_magic_perlfunc *)mag;
170 0           dSP;
171 0           ENTER; SAVETMPS;
172 0 0         PUSHMARK(SP);
173 0           perl_call_sv(magp->sv, G_DISCARD | G_NOARGS);
174 0 0         FREETMPS; LEAVE;
175 0           return NULL;
176             }
177              
178             static pdl_magic_vtable svmagic_vtable = {
179             svmagic_cast,
180             NULL
181             };
182              
183 0           pdl_magic *pdl_add_svmagic(pdl *it,SV *func)
184             {
185             AV *av;
186 0           pdl_magic_perlfunc *ptr = malloc(sizeof(pdl_magic_perlfunc));
187 0 0         if (!ptr) return NULL;
188 0           ptr->what = PDL_MAGIC_MARKCHANGED | PDL_MAGIC_DELAYED;
189 0           ptr->vtable = &svmagic_vtable;
190 0           ptr->sv = newSVsv(func);
191 0           ptr->pdl = it;
192 0           ptr->next = NULL;
193 0           pdl__magic_add(it,(pdl_magic *)ptr);
194 0 0         if(it->state & PDL_ANYCHANGED)
195 0           pdl_add_delayed_magic((pdl_magic *)ptr);
196             /* In order to have our SV destroyed in time for the interpreter, */
197             /* XXX Work this out not to memleak */
198 0           av = perl_get_av("PDL::disposable_svmagics",TRUE);
199 0           av_push(av,ptr->sv);
200 0           return (pdl_magic *)ptr;
201             }
202              
203             #ifdef PDL_PTHREAD
204              
205             /**************
206             *
207             * pthreads
208             *
209             */
210              
211             typedef struct ptarg {
212             pdl_magic_pthread *mag;
213             pdl_error (*func)(pdl_trans *);
214             pdl_trans *t;
215             int no;
216             pdl_error error_return;
217             } ptarg;
218              
219 1           int pdl_pthreads_enabled(void) {return 1;}
220              
221              
222 572           static void *pthread_perform(void *vp) {
223 572           struct ptarg *p = (ptarg *)vp;
224 572 50         PDLDEBUG_f(printf("STARTING THREAD %d (%lu)\n",p->no, (long unsigned)pthread_self()));
225 572           pthread_setspecific(p->mag->key,(void *)&(p->no));
226             int oldtype; /* don't care but must supply */
227 572           pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
228 572           p->error_return = (p->func)(p->t);
229 572 50         PDLDEBUG_f(printf("ENDING THREAD %d (%lu)\n",p->no, (long unsigned)pthread_self()));
230 572           return NULL;
231             }
232              
233 133           int pdl_magic_thread_nthreads(pdl *it, PDL_Indx *nthdim) {
234 133           pdl_magic_pthread *ptr = (pdl_magic_pthread *)pdl__find_magic(it, PDL_MAGIC_THREADING);
235 133 50         if(!ptr) return 0;
236 133 100         if (nthdim) *nthdim = ptr->nthdim;
237 133           return ptr->nthreads;
238             }
239              
240 3073           int pdl_magic_get_thread(pdl *it) {
241 3073           pdl_magic_pthread *ptr = (pdl_magic_pthread *)pdl__find_magic(it, PDL_MAGIC_THREADING);
242 3073 50         if(!ptr) return -1;
243 3073           int *p = (int*)pthread_getspecific(ptr->key);
244 3073 50         if(!p) return -1;
245 3073           return *p;
246             }
247              
248 115           pdl_error pdl_magic_thread_cast(pdl *it,pdl_error (*func)(pdl_trans *),pdl_trans *t, pdl_broadcast *broadcast) {
249 115           pdl_error PDL_err = {0, NULL, 0};
250 115 50         PDL_BRC_CHKMAGIC(broadcast);
    0          
251 115           int clearMagic = 0; /* Flag = 1 if we are temporarily creating pthreading magic in the
252             supplied pdl. */
253 115           pdl_magic_pthread *ptr = (pdl_magic_pthread *)pdl__find_magic(it, PDL_MAGIC_THREADING);
254 115 50         if(!ptr) {
255             /* Magic doesn't exist, create it
256             Probably was deleted before the transformation performed, due to
257             pdl lazy evaluation.
258             */
259              
260 0 0         PDL_RETERROR(PDL_err, pdl_add_threading_magic(it, broadcast->mag_nth, broadcast->mag_nthr));
261 0           clearMagic = 1; /* Set flag to delete magic later */
262              
263             /* Try to get magic again */
264 0           ptr = (pdl_magic_pthread *)pdl__find_magic(it, PDL_MAGIC_THREADING);
265              
266 0 0         if(!ptr) {return pdl_make_error_simple(PDL_EFATAL, "Invalid pdl_magic_thread_cast!");}
267              
268             }
269              
270 115           pthread_t tp[broadcast->mag_nthr];
271 115           ptarg tparg[broadcast->mag_nthr];
272 115           pthread_key_create(&(ptr->key),NULL);
273             /* Get the pthread ID of this main thread we are in.
274             * Any barf, warn, etc calls in the spawned pthreads can use this
275             * to tell if it's a spawned pthread
276             */
277 115           pdl_main_pthreadID = pthread_self();
278 115           done_pdl_main_pthreadID_init = 1;
279              
280 115 50         PDLDEBUG_f(printf("CREATING THREADS, ME: TBD, key: %ld\n", (unsigned long)(ptr->key)));
281 115           PDL_Indx i, last_pthread = -1;
282 687 100         for(i=0; imag_nthr; i++) {
283 572           tparg[i].mag = ptr;
284 572           tparg[i].func = func;
285 572           tparg[i].t = t;
286 572           tparg[i].no = i;
287 572           tparg[i].error_return = PDL_err;
288 572 50         if (pthread_create(tp+i, NULL, pthread_perform, tparg+i))
289 0           break;
290 572           last_pthread = i;
291             }
292 115 50         if (last_pthread < broadcast->mag_nthr-1) {
293             /* went wrong, cancel ones that started */
294 0 0         for (i=0; i <= last_pthread; i++)
295 0           pthread_cancel(tp[i]);
296             }
297              
298 115 50         PDLDEBUG_f(printf("JOINING THREADS, ME: TBD, key: %ld\n", (unsigned long)(ptr->key)));
299 687 100         for(i=0; i<=last_pthread; i++) {
300 572           pthread_join(tp[i], NULL);
301             }
302 115 50         PDLDEBUG_f(printf("FINISHED THREADS, ME: TBD, key: %ld\n", (unsigned long)(ptr->key)));
303              
304 115           pthread_key_delete((ptr->key));
305 115           done_pdl_main_pthreadID_init = 0;
306              
307             /* Remove pthread magic if we created in this function */
308 115 50         if( clearMagic ){
309 0 0         PDL_RETERROR(PDL_err, pdl_add_threading_magic(it, -1, -1));
310             }
311              
312             #define handle_deferred_errors(type, action) \
313             do{ \
314             if(pdl_pthread_##type##_msgs_len != 0) \
315             { \
316             pdl_pthread_##type##_msgs_len = 0; \
317             action; \
318             free(pdl_pthread_##type##_msgs); \
319             pdl_pthread_##type##_msgs = NULL; \
320             } \
321             } while(0)
322              
323 115 50         handle_deferred_errors(warn, pdl_pdl_warn("%s", pdl_pthread_warn_msgs));
324 115 50         if (last_pthread < broadcast->mag_nthr-1)
325 0           return pdl_make_error_simple(PDL_EFATAL, "Failed to create at least one thread, aborting");
326 115 50         handle_deferred_errors(barf, PDL_err = pdl_error_accumulate(PDL_err, pdl_make_error(PDL_EUSERERROR, "%s", pdl_pthread_barf_msgs)));
327 687 100         for(i=0; imag_nthr; i++) {
328 572           PDL_err = pdl_error_accumulate(PDL_err, tparg[i].error_return);
329             }
330 115           return PDL_err;
331             }
332              
333             /* Function to remove threading magic (added by pdl_add_threading_magic) */
334 26           pdl_error pdl_rm_threading_magic(pdl *it)
335             {
336 26           pdl_error PDL_err = {0, NULL, 0};
337 26           pdl_magic_pthread *ptr = (pdl_magic_pthread *)pdl__find_magic(it, PDL_MAGIC_THREADING);
338             /* Don't do anything if threading magic not found */
339 26 100         if( !ptr) return PDL_err;
340             /* Remove magic */
341 25 50         PDL_RETERROR(PDL_err, pdl__magic_rm(it, (pdl_magic *) ptr));
342             /* Free magic */
343 25           free( ptr );
344 25           return PDL_err;
345             }
346              
347             /* Function to add threading magic (i.e. identify which PDL dimension should
348             be pthreaded and how many pthreads to create
349             Note: If nthdim and nthreads = -1 then any pthreading magic is removed */
350 116           pdl_error pdl_add_threading_magic(pdl *it,PDL_Indx nthdim,PDL_Indx nthreads)
351             {
352 116           pdl_error PDL_err = {0, NULL, 0};
353             pdl_magic_pthread *ptr;
354             /* Remove threading magic if called with parms -1, -1 */
355 116 100         if( (nthdim == -1) && ( nthreads == -1 ) ){
    50          
356 26 50         PDL_RETERROR(PDL_err, pdl_rm_threading_magic(it));
357 26           return PDL_err;
358             }
359 90           ptr = malloc(sizeof(pdl_magic_pthread));
360 90 50         if (!ptr) return pdl_make_error_simple(PDL_EFATAL, "Out of memory");
361 90           ptr->what = PDL_MAGIC_THREADING;
362 90           ptr->vtable = NULL;
363 90           ptr->next = NULL;
364 90           ptr->nthdim = nthdim;
365 90           ptr->nthreads = nthreads;
366 90           pdl__magic_add(it,(pdl_magic *)ptr);
367 90           return PDL_err;
368             }
369              
370 280           char pdl_pthread_main_thread(void) {
371 280 100         return !done_pdl_main_pthreadID_init || pthread_equal( pdl_main_pthreadID, pthread_self() );
    50          
372             }
373              
374             // Barf/warn function for deferred barf message handling during pthreading We
375             // can't barf/warn during pthreading, because perl-level code isn't
376             // threadsafe. This routine does nothing if we're in the main thread (allowing
377             // the caller to barf normally, since there are not threading issues then). If
378             // we're in a worker thread, this routine stores the message for main-thread
379             // reporting later
380 165           int pdl_pthread_barf_or_warn(const char* pat, int iswarn, va_list *args)
381             {
382             char** msgs;
383             size_t* len;
384              
385             /* Don't do anything if we are in the main pthread */
386 165 50         if (pdl_pthread_main_thread()) return 0;
387              
388 0 0         if(iswarn)
389             {
390 0           msgs = &pdl_pthread_warn_msgs;
391 0           len = &pdl_pthread_warn_msgs_len;
392             }
393             else
394             {
395 0           msgs = &pdl_pthread_barf_msgs;
396 0           len = &pdl_pthread_barf_msgs_len;
397             }
398              
399 0           size_t extralen = vsnprintf(NULL, 0, pat, *args);
400             // add the new complaint to the list
401 0           pdl_pthread_realloc_vsnprintf(msgs, len, extralen, pat, args, 1);
402              
403 0 0         if(iswarn)
404             {
405             /* Return 1, indicating we have handled the warn messages */
406 0           return(1);
407             }
408              
409             /* Exit the current pthread. Since this was a barf call, and we should be halting execution */
410 0           pthread_exit(NULL);
411             return 0;
412             }
413              
414 1           void pdl_pthread_realloc_vsnprintf(char **p, size_t *len, size_t extralen, const char *pat, va_list *args, char add_newline) {
415             static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
416 1           pthread_mutex_lock( &mutex );
417             /* (For windows, we first #undef realloc
418             so that the system realloc function is used instead of the PerlMem_realloc
419             macro. This currently works fine, though could conceivably require some
420             tweaking in the future if it's found to cause any problem.) */
421             #ifdef WIN32
422             #undef realloc
423             #endif
424 1 50         if (add_newline) extralen += 1;
425 1           extralen += 1; /* +1 for '\0' at end */
426 1           *p = realloc(*p, *len + extralen);
427 1           vsnprintf(*p + *len, extralen, pat, *args);
428 1           *len += extralen; /* update the length-so-far, includes '\0' */
429 1 50         if (add_newline) (*p)[*len-2] = '\n';
430 1           (*p)[*len-1] = '\0';
431 1           pthread_mutex_unlock( &mutex );
432 1           }
433              
434 1           void pdl_pthread_free(void *p) {
435             #ifdef WIN32 /* same reasons as above */
436             #undef free
437             #endif
438             static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
439 1           pthread_mutex_lock( &mutex );
440 1           free(p);
441 1           pthread_mutex_unlock( &mutex );
442 1           }
443              
444             /* copied from git@github.com:git/git.git 2.34-ish thread-util.c */
445             /* changed GIT_WINDOWS_NATIVE to WIN32 */
446             #if defined(hpux) || defined(__hpux) || defined(_hpux)
447             # include
448             #endif
449             /*
450             * By doing this in two steps we can at least get
451             * the function to be somewhat coherent, even
452             * with this disgusting nest of #ifdefs.
453             */
454             #ifndef _SC_NPROCESSORS_ONLN
455             # ifdef _SC_NPROC_ONLN
456             # define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
457             # elif defined _SC_CRAY_NCPU
458             # define _SC_NPROCESSORS_ONLN _SC_CRAY_NCPU
459             # endif
460             #endif
461 80           int pdl_online_cpus(void)
462             {
463             #ifdef WIN32
464             SYSTEM_INFO info;
465             GetSystemInfo(&info);
466             if ((int)info.dwNumberOfProcessors > 0)
467             return (int)info.dwNumberOfProcessors;
468             #elif defined(hpux) || defined(__hpux) || defined(_hpux)
469             struct pst_dynamic psd;
470             if (!pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0))
471             return (int)psd.psd_proc_cnt;
472             #elif defined(HAVE_BSD_SYSCTL) && defined(HW_NCPU)
473             int mib[2];
474             size_t len;
475             int cpucount;
476             mib[0] = CTL_HW;
477             # ifdef HW_AVAILCPU
478             mib[1] = HW_AVAILCPU;
479             len = sizeof(cpucount);
480             if (!sysctl(mib, 2, &cpucount, &len, NULL, 0))
481             return cpucount;
482             # endif /* HW_AVAILCPU */
483             mib[1] = HW_NCPU;
484             len = sizeof(cpucount);
485             if (!sysctl(mib, 2, &cpucount, &len, NULL, 0))
486             return cpucount;
487             #endif /* defined(HAVE_BSD_SYSCTL) && defined(HW_NCPU) */
488             #ifdef _SC_NPROCESSORS_ONLN
489             long ncpus;
490 80 50         if ((ncpus = (long)sysconf(_SC_NPROCESSORS_ONLN)) > 0)
491 80           return (int)ncpus;
492             #endif
493 0           return 1;
494             }
495              
496             #else
497             /* Dummy versions */
498             pdl_error pdl_add_threading_magic(pdl *it,PDL_Indx nthdim,PDL_Indx nthreads) {pdl_error PDL_err = {0,NULL,0}; return PDL_err;}
499             char pdl_pthread_main_thread() { return 1; }
500             int pdl_magic_get_thread(pdl *it) {return 0;}
501             pdl_error pdl_magic_thread_cast(pdl *it,pdl_error (*func)(pdl_trans *),pdl_trans *t, pdl_broadcast *broadcast) {pdl_error PDL_err = {0,NULL,0}; return PDL_err;}
502             int pdl_magic_thread_nthreads(pdl *it,PDL_Indx *nthdim) {return 0;}
503             int pdl_pthreads_enabled() {return 0;}
504             int pdl_pthread_barf_or_warn(const char* pat, int iswarn, va_list *args){ return 0;}
505             int pdl_online_cpus() {return 1;}
506             #endif
507              
508             /***************************
509             *
510             * Delete magic
511             *
512             */
513              
514 0           static void *delete_mmapped_cast(pdl_magic *mag)
515             {
516 0           pdl_magic_deletedata *magp = (pdl_magic_deletedata *)mag;
517 0           magp->func(magp->pdl, magp->param);
518 0           return NULL;
519             }
520              
521             struct pdl_magic_vtable deletedatamagic_vtable = {
522             delete_mmapped_cast,
523             NULL
524             };
525              
526 0           pdl_error pdl_add_deletedata_magic(pdl *it, void (*func)(pdl *, Size_t param), Size_t param)
527             {
528 0           pdl_error PDL_err = {0, NULL, 0};
529 0           pdl_magic_deletedata *ptr = malloc(sizeof(pdl_magic_deletedata));
530 0 0         if (!ptr) return pdl_make_error_simple(PDL_EFATAL, "Out of memory");
531 0           ptr->what = PDL_MAGIC_DELETEDATA;
532 0           ptr->vtable = &deletedatamagic_vtable;
533 0           ptr->pdl = it;
534 0           ptr->func = func;
535 0           ptr->param = param;
536 0           pdl__magic_add(it, (pdl_magic *)ptr);
537 0           return PDL_err;
538             }