BRL-CAD
parallel.c
Go to the documentation of this file.
1 /* P A R A L L E L . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2004-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 
21 #include "common.h"
22 
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include <math.h>
26 #include <string.h>
27 #include <signal.h>
28 
29 #ifdef HAVE_SYS_TIME_H
30 # include <sys/time.h>
31 #endif
32 
33 #ifdef HAVE_SYS_RESOURCE_H
34 # include <sys/resource.h>
35 #endif
36 
37 #ifdef linux
38 # include <sys/types.h>
39 # ifdef HAVE_SYS_WAIT_H
40 # include <sys/wait.h>
41 # endif
42 # include <sys/stat.h>
43 # include <sys/sysinfo.h>
44 #endif
45 
46 #if defined(__FreeBSD__) || defined(__OpenBSD__)
47 # include <sys/types.h>
48 # include <sys/param.h>
49 # include <sys/sysctl.h>
50 # ifdef HAVE_SYS_WAIT_H
51 # include <sys/wait.h>
52 # endif
53 # include <sys/stat.h>
54 #endif
55 
56 #ifdef __APPLE__
57 # include <sys/types.h>
58 # ifdef HAVE_SYS_WAIT_H
59 # include <sys/wait.h>
60 # endif
61 # include <sys/stat.h>
62 # include <sys/param.h>
63 # include <sys/sysctl.h>
64 #endif
65 
66 #ifdef __sp3__
67 # include <sys/types.h>
68 # include <sys/sysconfig.h>
69 # include <sys/var.h>
70 #endif
71 
72 #ifdef HAVE_SYS_TYPES_H
73 # include <sys/types.h>
74 #endif
75 #ifdef HAVE_ULOCKS_H
76 # include <ulocks.h>
77 #endif
78 #ifdef HAVE_SYS_SYSMP_H
79 # include <sys/sysmp.h> /* for sysmp() */
80 #endif
81 
82 #ifdef HAVE_SYS_WAIT_H
83 # include <sys/wait.h>
84 #endif
85 
86 #ifdef HAVE_SCHED_H
87 # include <sched.h>
88 #else
89 # ifdef HAVE_SYS_SCHED_H
90 # include <sys/sched.h>
91 # endif
92 #endif
93 
94 /*
95  * multithreading support for SunOS 5.X / Solaris 2.x
96  */
97 #if defined(SUNOS) && SUNOS >= 52
98 # include <sys/unistd.h>
99 # include <thread.h>
100 # include <synch.h>
101 # define rt_thread_t thread_t
102 #endif /* SUNOS */
103 
104 /*
105  * multithread support built on POSIX Threads (pthread) library.
106  */
107 #ifdef HAVE_PTHREAD_H
108 # include <pthread.h>
109 # define rt_thread_t pthread_t
110 #endif
111 
112 #ifdef WIN32
113 # define rt_thread_t HANDLE
114 #endif
115 
116 #include "bio.h"
117 
118 #include "bu/debug.h"
119 #include "bu/log.h"
120 #include "bu/malloc.h"
121 #include "bu/parallel.h"
122 #include "bu/str.h"
123 
124 #include "./parallel.h"
125 
126 
127 typedef enum {
131 
132 
134  int id; /* cpu+1 */
135  int parent;
136  size_t lim;
137  size_t started;
138  size_t finished;
139 };
140 
141 
142 struct thread_data {
143  void (*user_func)(int, void *);
144  void *user_arg;
145  int cpu_id;
146  int affinity;
148 };
149 
150 
151 int
153 {
154  return thread_get_cpu();
155 }
156 
157 
158 int
160 {
161  /* this routine is deprecated, do not use. */
162  return 0;
163 }
164 
165 
166 void
167 bu_nice_set(int newnice)
168 {
169 #ifdef HAVE_SETPRIORITY
170  int opri, npri;
171 
172 # ifndef PRIO_PROCESS /* necessary for linux */
173 # define PRIO_PROCESS 0 /* From /usr/include/sys/resource.h */
174 # endif
175  opri = getpriority(PRIO_PROCESS, 0);
176  setpriority(PRIO_PROCESS, 0, newnice);
177  npri = getpriority(PRIO_PROCESS, 0);
178 
179  if (UNLIKELY(bu_debug)) {
180  bu_log("bu_nice_set() Priority changed from %d to %d\n", opri, npri);
181  }
182 
183 #else /* !HAVE_SETPRIORITY */
184  /* no known means to change the nice value */
185  if (UNLIKELY(bu_debug)) {
186  bu_log("bu_nice_set(%d) Priority NOT changed\n", newnice);
187  }
188 #endif /* _WIN32 */
189 }
190 
191 
192 size_t
194 {
195  int ncpu = -1;
196 
197 #ifdef PARALLEL
198 
199 # if defined(__sp3__)
200  if (ncpu < 0) {
201  int status;
202  int cmd;
203  int parmlen;
204  struct var p;
205 
206  cmd = SYS_GETPARMS;
207  parmlen = sizeof(struct var);
208  if (sysconfig(cmd, &p, parmlen) != 0) {
209  bu_bomb("bu_parallel(): sysconfig error for sp3");
210  }
211  ncpu = p.v_ncpus;
212  }
213 # endif /* __sp3__ */
214 
215 
216 # ifdef __FreeBSD__
217  if (ncpu < 0) {
218  int maxproc;
219  size_t len;
220  len = 4;
221  if (sysctlbyname("hw.ncpu", &maxproc, &len, NULL, 0) == -1) {
222  perror("sysctlbyname");
223  } else {
224  ncpu = maxproc;
225  }
226  }
227 # endif
228 
229 
230 # if defined(__APPLE__)
231  if (ncpu < 0) {
232  size_t len;
233  int maxproc;
234  int mib[] = {CTL_HW, HW_AVAILCPU};
235 
236  len = sizeof(maxproc);
237  if (sysctl(mib, 2, &maxproc, &len, NULL, 0) == -1) {
238  perror("sysctl");
239  } else {
240  ncpu = maxproc; /* should be able to get sysctl to return maxproc */
241  }
242  }
243 # endif /* __ppc__ */
244 
245 
246 # if defined(HAVE_GET_NPROCS)
247  if (ncpu < 0) {
248  ncpu = get_nprocs(); /* GNU extension from sys/sysinfo.h */
249  }
250 # endif
251 
252 
253  /*
254  * multithreading support for SunOS 5.X / Solaris 2.x
255  */
256 # if defined(_SC_NPROCESSORS_ONLN)
257  /* SUNOS and linux (and now Mac 10.6+) */
258  if (ncpu < 0) {
259  ncpu = sysconf(_SC_NPROCESSORS_ONLN);
260  if (ncpu < 0) {
261  perror("Unable to get the number of available CPUs");
262  }
263  }
264 #endif
265 
266 
267 #if defined(_SC_NPROC_ONLN)
268  if (ncpu < 0) {
269  ncpu = sysconf(_SC_NPROC_ONLN);
270  if (ncpu < 0) {
271  perror("Unable to get the number of available CPUs");
272  }
273  }
274 #endif
275 
276 
277 # if defined(linux)
278  if (ncpu < 0) {
279  /* old linux method */
280  /*
281  * Ultra-kludgey way to determine the number of cpus in a
282  * linux box--count the number of processor entries in
283  * /proc/cpuinfo!
284  */
285 
286 # define CPUINFO_FILE "/proc/cpuinfo"
287  FILE *fp;
288  char buf[128];
289 
290  fp = fopen (CPUINFO_FILE, "r");
291 
292  if (fp == NULL) {
293  perror (CPUINFO_FILE);
294  } else {
295  ncpu = 0;
296  while (bu_fgets(buf, 80, fp) != NULL) {
297  if (bu_strncmp (buf, "processor", 9) == 0) {
298  ncpu++;
299  }
300  }
301  fclose (fp);
302  }
303  }
304 # endif
305 
306 
307 # if defined(_WIN32)
308  /* Windows */
309  if (ncpu < 0) {
310  SYSTEM_INFO sysinfo;
311 
312  GetSystemInfo(&sysinfo);
313  ncpu = (int)sysinfo.dwNumberOfProcessors;
314  }
315 # endif
316 
317 
318 #endif /* PARALLEL */
319 
321  /* do not use bu_log() here, this can get called before semaphores are initialized */
322  fprintf(stderr, "bu_avail_cpus: counted %d cpus.\n", ncpu);
323  }
324 
325  if (LIKELY(ncpu > 0)) {
326  return ncpu;
327  }
328 
329  /* non-PARALLEL */
330  return 1;
331 }
332 
333 
334 /**********************************************************************/
335 
336 
337 #ifdef PARALLEL
338 
339 /* this function provides book-keeping so that we give out unique
340  * thread identifiers and for tracking a thread's parent context.
341  */
342 static struct parallel_info *
343 parallel_mapping(parallel_action_t action, int id, int max)
344 {
345  /* container for keeping track of recursive invocation data, limits, current values */
346  static struct parallel_info mapping[MAX_PSW] = {{0,0,0,0,0}};
347  int got_cpu;
348 
349  switch (action) {
350  case PARALLEL_GET:
351  if (id < 0) {
353  for (got_cpu = 1; got_cpu < MAX_PSW; got_cpu++) {
354  if (mapping[got_cpu].id == 0) {
355  mapping[got_cpu].id = got_cpu;
356  break;
357  }
358  }
360 
361  if (got_cpu >= MAX_PSW)
362  bu_bomb("Compile-time parallelism limit reached. Unable to track more threading.\n");
363 
364  mapping[got_cpu].started = mapping[got_cpu].finished = 0;
365  mapping[got_cpu].parent = bu_parallel_id();
366 
367  } else {
368  got_cpu = id;
369  if (mapping[got_cpu].id != got_cpu) {
370  /* presumably id == 0 */
371  mapping[got_cpu].id = got_cpu;
372  }
373  }
374 
375  if (mapping[got_cpu].lim == 0 && max > 0)
376  mapping[got_cpu].lim = max;
377 
378  return &mapping[got_cpu];
379 
380  case PARALLEL_PUT:
381  mapping[id].started = mapping[id].finished = mapping[id].lim = mapping[id].parent = 0;
382  mapping[id].id = 0; /* separate to avoid race */
383  }
384 
385  return NULL;
386 }
387 
388 
389 static void
390 parallel_wait_for_slot(int throttle, struct parallel_info *parent, size_t max_threads)
391 {
392  size_t threads = max_threads;
393 
394  while (1) {
395  threads = parent->started - parent->finished;
396 
397  /* bu_log("threads=%d (start %d - done %d)\n", threads, parent->started, parent->finished); */
398 
399  if (threads < max_threads || !throttle) {
400  return;
401  }
402  sleep(1);
403  }
404 }
405 
406 
407 HIDDEN void
408 parallel_interface_arg(struct thread_data *user_thread_data)
409 {
410  /* keep track of our parallel ID number */
411  thread_set_cpu(user_thread_data->cpu_id);
412 
413  if (user_thread_data->affinity) {
414  int ret;
415  /* lock us onto a core corresponding to our parallel ID number */
416  ret = parallel_set_affinity(user_thread_data->cpu_id);
417  if (ret) {
418  bu_log("WARNING: encountered unexpected problem setting CPU affinity\n");
419  }
420  }
421 
423  user_thread_data->parent->started++;
425 
426  (*(user_thread_data->user_func))(user_thread_data->cpu_id, user_thread_data->user_arg);
427 
429  user_thread_data->parent->finished++;
431 
432  parallel_mapping(PARALLEL_PUT, user_thread_data->cpu_id, 0);
433 
434 }
435 
436 
437 #if defined(_WIN32)
438 /**
439  * A separate stub to call parallel_interface_arg that avoids a
440  * potential crash* on 64-bit windows and calls ExitThread to
441  * cleanly stop the thread.
442  * *See ThreadProc MSDN documentation.
443  */
444 HIDDEN DWORD
445 parallel_interface_arg_stub(struct thread_data *user_thread_data)
446 {
447  parallel_interface_arg(user_thread_data);
448  ExitThread(0);
449  return 0; /* Extraneous */
450 }
451 #endif
452 
453 
454 #endif /* PARALLEL */
455 
456 
457 void
458 bu_parallel(void (*func)(int, void *), int ncpu, void *arg)
459 {
460 #ifndef PARALLEL
461 
462  if (!func)
463  return; /* nothing to do */
464 
465  bu_log("bu_parallel(%d., %p): Not compiled for PARALLEL machine, running single-threaded\n", ncpu, arg);
466  /* do the work anyways */
467  (*func)(0, arg);
468 
469 #else
470 
471  struct thread_data *thread_context;
472  rt_thread_t thread_tbl[MAX_PSW];
473  int avail_cpus = 1;
474  int x;
475  int i;
476 
477  /* number of threads created/ended */
478  int nthreadc;
479  int nthreade;
480 
481  char *libbu_affinity = NULL;
482 
483  /* OFF by default as modern schedulers are smarter than this. */
484  int affinity = 0;
485 
486  /* ncpu == 0 means throttle our thread creation as slots become available */
487  int throttle = 0;
488 
489  struct parallel_info *parent;
490 
491  rt_thread_t thread;
492 
493  if (!func)
494  return; /* nothing to do */
495 
497  bu_log("bu_parallel(%d, %p)\n", ncpu, arg);
498 
499  if (ncpu > MAX_PSW) {
500  bu_log("WARNING: bu_parallel() ncpu(%d) > MAX_PSW(%d), adjusting ncpu\n", ncpu, MAX_PSW);
501  ncpu = MAX_PSW;
502  }
503 
504  libbu_affinity = getenv("LIBBU_AFFINITY");
505  if (libbu_affinity)
506  affinity = (int)strtol(libbu_affinity, NULL, 0x10);
507  if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) {
508  if (affinity)
509  bu_log("CPU affinity enabled. (LIBBU_AFFINITY=%d)\n", affinity);
510  else
511  bu_log("CPU affinity disabled.\n");
512  }
513 
514  /* if we're in debug mode, allow additional cpus */
515  if (!(bu_debug & BU_DEBUG_PARALLEL)) {
516  /* otherwise, limit ourselves to what is actually available */
517  avail_cpus = bu_avail_cpus();
518  if (ncpu > avail_cpus) {
519  bu_log("%d cpus requested, but only %d available\n", ncpu, avail_cpus);
520  ncpu = avail_cpus;
521  }
522  }
523 
524  parent = parallel_mapping(PARALLEL_GET, bu_parallel_id(), (size_t)ncpu);
525 
526  if (ncpu < 1) {
527  /* want to maximize threading potential, but have to throttle
528  * thread creation. what is our parallelization limit?
529  */
530  throttle = 1;
531 
532  /* any "zero" limit scopes propagate upward */
533  while (parent->lim == 0 && parent->id > 0) {
534  parent = parallel_mapping(PARALLEL_GET, parent->parent, (size_t)ncpu);
535  }
536 
537  /* if the top-most parent is unspecified, use all available cpus */
538  if (parent->lim == 0) {
539  ncpu = bu_avail_cpus();
540  } else {
541  ncpu = parent->lim;
542  }
543 
544  /* starting a "zero" bu_parallel means we get one worker
545  * thread back (for this thread)
546  */
548  if (parent->started > 0)
549  parent->started--;
551  } else if (ncpu == 1) {
552  /* single cpu case bypasses nearly everything, just invoke */
553  (*func)(0, arg);
554 
555  parallel_mapping(PARALLEL_PUT, bu_parallel_id(), 0);
556  return;
557  }
558 
559  thread_context = (struct thread_data *)bu_calloc(ncpu, sizeof(*thread_context), "struct thread_data *thread_context");
560 
561  /* Fill in the data of thread_context structures of all threads */
562  for (x = 0; x < ncpu; x++) {
563  struct parallel_info *next = parallel_mapping(PARALLEL_GET, -1, (size_t)ncpu);
564 
565  thread_context[x].user_func = func;
566  thread_context[x].user_arg = arg;
567  thread_context[x].cpu_id = next->id;
568  thread_context[x].affinity = affinity;
569  thread_context[x].parent = parent;
570  }
571 
572  /*
573  * multithreading support for SunOS 5.X / Solaris 2.x
574  */
575 # if defined(SUNOS) && SUNOS >= 52
576 
577  nthreadc = 0;
578 
579  /* Give the thread system a hint... */
580  {
581  static int concurrency = 0; /* Max concurrency we have set */
582  if (ncpu > concurrency) {
583  if (thr_setconcurrency(ncpu)) {
584  bu_log("ERROR parallel.c/bu_parallel(): thr_setconcurrency(%d) failed\n",
585  ncpu);
586  /* Not much to do, lump it */
587  } else {
588  concurrency = ncpu;
589  }
590  }
591  }
592 
593  /* Create the threads */
594  for (x = 0; x < ncpu; x++) {
595  parallel_wait_for_slot(throttle, parent, (size_t)ncpu);
596 
597  if (thr_create(0, 0, (void *(*)(void *))parallel_interface_arg, &thread_context[x], 0, &thread)) {
598  bu_log("ERROR: bu_parallel: thr_create(0x0, 0x0, 0x%x, 0x0, 0, 0x%x) failed for processor thread # %d\n",
599  parallel_interface_arg, &thread, x);
600  /* Not much to do, lump it */
601  } else {
602  if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL))
603  bu_log("bu_parallel(): created thread: (thread: 0x%x) (loop:%d) (nthreadc:%d)\n",
604  thread, x, nthreadc);
605 
606  thread_tbl[nthreadc] = thread;
607  nthreadc++;
608  }
609  }
610 
611  if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL))
612  for (i = 0; i < nthreadc; i++)
613  bu_log("bu_parallel(): thread_tbl[%d] = 0x%x\n", i, thread_tbl[i]);
614 
615  /*
616  * Wait for completion of all threads. We don't wait for threads
617  * in order. We wait for any old thread but we keep track of how
618  * many have returned and whether it is one that we started
619  */
620  nthreade = 0;
621  for (x = 0; x < nthreadc; x++) {
622  if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL))
623  bu_log("bu_parallel(): waiting for thread to complete:\t(loop:%d) (nthreadc:%d) (nthreade:%d)\n",
624  x, nthreadc, nthreade);
625 
626  if (thr_join((rt_thread_t)0, &thread, NULL)) {
627  /* badness happened */
628  perror("thr_join");
629  bu_log("thr_join() failed");
630  }
631 
632  /* Check to see if this is one the threads we created */
633  for (i = 0; i < nthreadc; i++) {
634  if (thread_tbl[i] == thread) {
635  thread_tbl[i] = (rt_thread_t)-1;
636  nthreade++;
637  break;
638  }
639  }
640 
641  if ((thread_tbl[i] != (rt_thread_t)-1) && i < nthreadc) {
642  bu_log("bu_parallel(): unknown thread %d completed.\n",
643  thread);
644  }
645 
646  if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL))
647  bu_log("bu_parallel(): thread completed: (thread: %d)\t(loop:%d) (nthreadc:%d) (nthreade:%d)\n",
648  thread, x, nthreadc, nthreade);
649  }
650 
651  if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL))
652  bu_log("bu_parallel(): %d threads created. %d threads exited.\n",
653  nthreadc, nthreade);
654 # endif /* SUNOS */
655 
656 # if defined(HAVE_PTHREAD_H)
657 
658  /* Create the posix threads.
659  *
660  * Start at 1 so we can treat the parent as thread 0.
661  */
662  nthreadc = 0;
663  for (x = 0; x < ncpu; x++) {
664  pthread_attr_t attrs;
665  pthread_attr_init(&attrs);
666  pthread_attr_setstacksize(&attrs, 10*1024*1024);
667 
668  parallel_wait_for_slot(throttle, parent, (size_t)ncpu);
669 
670  if (pthread_create(&thread, &attrs, (void *(*)(void *))parallel_interface_arg, &thread_context[x])) {
671  bu_log("ERROR: bu_parallel: pthread_create(0x0, 0x0, 0x%lx, 0x0, 0, %p) failed for processor thread # %d\n",
672  (unsigned long int)parallel_interface_arg, (void *)&thread, x);
673 
674  } else {
675  if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) {
676  bu_log("bu_parallel(): created thread: (thread: %p) (loop: %d) (nthreadc: %d)\n",
677  (void*)thread, x, nthreadc);
678  }
679  thread_tbl[nthreadc] = thread;
680  nthreadc++;
681  }
682 
683  /* done with the attributes after create */
684  pthread_attr_destroy(&attrs);
685  }
686 
687  if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL)) {
688  for (i = 0; i < nthreadc; i++) {
689  bu_log("bu_parallel(): thread_tbl[%d] = %p\n", i, (void *)thread_tbl[i]);
690  }
691 # ifdef SIGINFO
692  /* may be BSD-only (calls _thread_dump_info()) */
693  raise(SIGINFO);
694 # endif
695  }
696 
697  /*
698  * Wait for completion of all threads.
699  * Wait for them in order.
700  */
701  nthreade = 0;
702  for (x = 0; x < nthreadc; x++) {
703  int ret;
704 
705  if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL))
706  bu_log("bu_parallel(): waiting for thread %p to complete:\t(loop:%d) (nthreadc:%d) (nthreade:%d)\n",
707  (void *)thread_tbl[x], x, nthreadc, nthreade);
708 
709  if ((ret = pthread_join(thread_tbl[x], NULL)) != 0) {
710  /* badness happened */
711  bu_log("pthread_join(thread_tbl[%d]=%p) ret=%d\n", x, (void *)thread_tbl[x], ret);
712  }
713 
714  nthreade++;
715  thread = thread_tbl[x];
716  thread_tbl[x] = (rt_thread_t)-1;
717 
718  if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL))
719  bu_log("bu_parallel(): thread completed: (thread: %p)\t(loop:%d) (nthreadc:%d) (nthreade:%d)\n",
720  (void *)thread, x, nthreadc, nthreade);
721 
722  }
723 
724  if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL))
725  bu_log("bu_parallel(): %d threads created. %d threads exited.\n", nthreadc, nthreade);
726 
727 # endif /* end if posix threads */
728 
729 
730 # ifdef WIN32
731  /* Create the Win32 threads */
732  nthreadc = 0;
733  for (i = 0; i < ncpu; i++) {
734  parallel_wait_for_slot(throttle, parent, (size_t)ncpu);
735 
736  thread = CreateThread(
737  NULL,
738  0,
739  (LPTHREAD_START_ROUTINE)parallel_interface_arg_stub,
740  &thread_context[i],
741  0,
742  NULL);
743 
744  thread_tbl[i] = thread;
745  nthreadc++;
746 
747  /* Ensure that all successfully created threads are in sequential order.*/
748  if (thread_tbl[i] == NULL) {
749  bu_log("bu_parallel(): Error in CreateThread, Win32 error code %d.\n", GetLastError());
750  --nthreadc;
751  }
752  }
753 
754 
755  {
756  /* Wait for other threads in the array */
757  DWORD returnCode;
758  returnCode = WaitForMultipleObjects(nthreadc, thread_tbl, TRUE, INFINITE);
759  if (returnCode == WAIT_FAILED) {
760  bu_log("bu_parallel(): Error in WaitForMultipleObjects, Win32 error code %d.\n", GetLastError());
761  }
762  }
763 
764  nthreade = 0;
765  for (x = 0; x < nthreadc; x++) {
766  int ret;
767  if ((ret = CloseHandle(thread_tbl[x]) == 0)) {
768  /* Thread didn't close properly if return value is zero; don't retry and potentially loop forever. */
769  bu_log("bu_parallel(): Error closing thread %d of %d, Win32 error code %d.\n", x, nthreadc, GetLastError());
770  }
771 
772  nthreade++;
773  thread_tbl[x] = (rt_thread_t)-1;
774  }
775 # endif /* end if Win32 threads */
776 
777  parallel_mapping(PARALLEL_PUT, bu_parallel_id(), 0);
778 
779  if (UNLIKELY(bu_debug & BU_DEBUG_PARALLEL))
780  bu_log("bu_parallel(%d) complete\n", ncpu);
781 
782  bu_free(thread_context, "struct thread_data *thread_context");
783 
784 #endif /* PARALLEL */
785 
786  return;
787 }
788 
789 
790 /*
791  * Local Variables:
792  * mode: C
793  * tab-width: 8
794  * indent-tabs-mode: t
795  * c-file-style: "stroustrup"
796  * End:
797  * ex: shiftwidth=4 tabstop=8
798  */
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
int thread_get_cpu(void)
Definition: thread.cpp:115
int parallel_set_affinity(int cpu)
Definition: affinity.c:56
#define BU_DEBUG_PARALLEL
Definition: debug.h:58
struct parallel_info * parent
Definition: parallel.c:147
#define LIKELY(expression)
Definition: common.h:261
void bu_semaphore_acquire(unsigned int i)
Definition: semaphore.c:180
Header file for the BRL-CAD common definitions.
void thread_set_cpu(int cpu)
Definition: thread.cpp:108
int bu_is_parallel(void)
Definition: parallel.c:159
#define HIDDEN
Definition: common.h:86
void(* user_func)(int, void *)
Definition: parallel.c:143
int bu_strncmp(const char *string1, const char *string2, size_t n)
Definition: str.c:191
int cpu_id
Definition: parallel.c:145
void * bu_calloc(size_t nelem, size_t elsize, const char *str)
Definition: malloc.c:321
void bu_nice_set(int newnice)
Definition: parallel.c:167
int bu_parallel_id(void)
Definition: parallel.c:152
void bu_semaphore_release(unsigned int i)
Definition: semaphore.c:218
size_t bu_avail_cpus(void)
Definition: parallel.c:193
#define MAX_PSW
Definition: parallel.h:48
#define BU_SEM_THREAD
Definition: parallel.h:182
parallel_action_t
Definition: parallel.c:127
int bu_debug
Definition: globals.c:87
void bu_parallel(void(*func)(int, void *), int ncpu, void *arg)
Definition: parallel.c:458
size_t finished
Definition: parallel.c:138
int affinity
Definition: parallel.c:146
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
char * bu_fgets(char *s, int size, FILE *stream)
Definition: fgets.c:31
size_t started
Definition: parallel.c:137
void bu_bomb(const char *str) _BU_ATTR_NORETURN
Definition: bomb.c:91
void * user_arg
Definition: parallel.c:144
size_t lim
Definition: parallel.c:136
#define TRUE
#define UNLIKELY(expression)
Definition: common.h:282