1 /*****************************************************************************
2 * thread.c : OS/2 back-end for LibVLC
3 *****************************************************************************
4 * Copyright (C) 1999-2011 VLC authors and VideoLAN
6 * Authors: KO Myung-Hun <komh@chollian.net>
7 * Jean-Marc Dressler <polux@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
9 * Gildas Bazin <gbazin@netcourrier.com>
14 * This program is free software; you can redistribute it and/or modify it
15 * under the terms of the GNU Lesser General Public License as published by
16 * the Free Software Foundation; either version 2.1 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public License
25 * along with this program; if not, write to the Free Software Foundation,
26 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27 *****************************************************************************/
33 #include <vlc_common.h>
42 #include <sys/types.h>
43 #include <sys/socket.h>
46 #include <sys/select.h>
48 #include <sys/builtin.h>
52 static vlc_threadvar_t thread_key
;
64 vlc_cleanup_t
*cleaners
;
66 void *(*entry
) (void *);
70 static void vlc_cancel_self (PVOID dummy
);
72 static ULONG
vlc_DosWaitEventSemEx( HEV hev
, ULONG ulTimeout
)
80 struct vlc_thread
*th
= vlc_thread_self ();
81 if( th
== NULL
|| !th
->killable
)
83 /* Main thread - cannot be cancelled anyway
84 * Alien thread - out of our control
85 * Cancel disabled thread - ignore cancel
87 if( hev
!= NULLHANDLE
)
88 return DosWaitEventSem( hev
, ulTimeout
);
90 return DosSleep( ulTimeout
);
94 if( hev
!= NULLHANDLE
)
96 asr
[ n
].hsemCur
= ( HSEM
)hev
;
100 asr
[ n
].hsemCur
= ( HSEM
)th
->cancel_event
;
101 asr
[ n
].ulUser
= 0xFFFF;
104 DosCreateMuxWaitSem( NULL
, &hmux
, n
, asr
, DCMW_WAIT_ANY
);
105 rc
= DosWaitMuxWaitSem( hmux
, ulTimeout
, &ulUser
);
106 DosCloseMuxWaitSem( hmux
);
110 if( ulUser
== 0xFFFF )
112 vlc_cancel_self( th
);
113 return ERROR_INTERRUPT
;
119 static ULONG
vlc_WaitForSingleObject (HEV hev
, ULONG ulTimeout
)
121 return vlc_DosWaitEventSemEx( hev
, ulTimeout
);
124 static ULONG
vlc_Sleep (ULONG ulTimeout
)
126 ULONG rc
= vlc_DosWaitEventSemEx( NULLHANDLE
, ulTimeout
);
128 return ( rc
!= ERROR_TIMEOUT
) ? rc
: 0;
131 static vlc_mutex_t super_mutex
;
132 static vlc_cond_t super_variable
;
133 extern vlc_rwlock_t config_lock
;
135 static void vlc_static_cond_destroy_all(void);
138 void _CRT_term(void);
140 unsigned long _System
_DLL_InitTerm(unsigned long, unsigned long);
142 unsigned long _System
_DLL_InitTerm(unsigned long hmod
, unsigned long flag
)
148 case 0 : /* Initialization */
149 if(_CRT_init() == -1)
152 vlc_mutex_init (&super_mutex
);
153 vlc_cond_init (&super_variable
);
154 vlc_threadvar_create (&thread_key
, NULL
);
155 vlc_rwlock_init (&config_lock
);
159 case 1 : /* Termination */
160 vlc_rwlock_destroy (&config_lock
);
161 vlc_threadvar_delete (&thread_key
);
162 vlc_cond_destroy (&super_variable
);
163 vlc_mutex_destroy (&super_mutex
);
164 vlc_static_cond_destroy_all ();
171 return 0; /* Failed */
175 void vlc_mutex_init( vlc_mutex_t
*p_mutex
)
177 /* This creates a recursive mutex. This is OK as fast mutexes have
178 * no defined behavior in case of recursive locking. */
179 DosCreateMutexSem( NULL
, &p_mutex
->hmtx
, 0, FALSE
);
180 p_mutex
->dynamic
= true;
183 void vlc_mutex_init_recursive( vlc_mutex_t
*p_mutex
)
185 DosCreateMutexSem( NULL
, &p_mutex
->hmtx
, 0, FALSE
);
186 p_mutex
->dynamic
= true;
190 void vlc_mutex_destroy (vlc_mutex_t
*p_mutex
)
192 assert (p_mutex
->dynamic
);
193 DosCloseMutexSem( p_mutex
->hmtx
);
196 void vlc_mutex_lock (vlc_mutex_t
*p_mutex
)
198 if (!p_mutex
->dynamic
)
199 { /* static mutexes */
200 int canc
= vlc_savecancel ();
201 assert (p_mutex
!= &super_mutex
); /* this one cannot be static */
203 vlc_mutex_lock (&super_mutex
);
204 while (p_mutex
->locked
)
206 p_mutex
->contention
++;
207 vlc_cond_wait (&super_variable
, &super_mutex
);
208 p_mutex
->contention
--;
210 p_mutex
->locked
= true;
211 vlc_mutex_unlock (&super_mutex
);
212 vlc_restorecancel (canc
);
216 DosRequestMutexSem(p_mutex
->hmtx
, SEM_INDEFINITE_WAIT
);
219 int vlc_mutex_trylock (vlc_mutex_t
*p_mutex
)
221 if (!p_mutex
->dynamic
)
222 { /* static mutexes */
225 assert (p_mutex
!= &super_mutex
); /* this one cannot be static */
226 vlc_mutex_lock (&super_mutex
);
227 if (!p_mutex
->locked
)
229 p_mutex
->locked
= true;
232 vlc_mutex_unlock (&super_mutex
);
236 return DosRequestMutexSem( p_mutex
->hmtx
, 0 ) ? EBUSY
: 0;
239 void vlc_mutex_unlock (vlc_mutex_t
*p_mutex
)
241 if (!p_mutex
->dynamic
)
242 { /* static mutexes */
243 assert (p_mutex
!= &super_mutex
); /* this one cannot be static */
245 vlc_mutex_lock (&super_mutex
);
246 assert (p_mutex
->locked
);
247 p_mutex
->locked
= false;
248 if (p_mutex
->contention
)
249 vlc_cond_broadcast (&super_variable
);
250 vlc_mutex_unlock (&super_mutex
);
254 DosReleaseMutexSem( p_mutex
->hmtx
);
257 /*** Condition variables ***/
258 typedef struct vlc_static_cond_t vlc_static_cond_t
;
260 struct vlc_static_cond_t
263 vlc_static_cond_t
*next
;
266 static vlc_static_cond_t
*static_condvar_start
= NULL
;
268 static void vlc_static_cond_init (vlc_cond_t
*p_condvar
)
270 vlc_mutex_lock (&super_mutex
);
272 if (p_condvar
->hev
== NULLHANDLE
)
274 vlc_cond_init (p_condvar
);
276 vlc_static_cond_t
*new_static_condvar
;
278 new_static_condvar
= malloc (sizeof (*new_static_condvar
));
279 if (unlikely (!new_static_condvar
))
282 memcpy (&new_static_condvar
->condvar
, p_condvar
, sizeof (*p_condvar
));
283 new_static_condvar
->next
= static_condvar_start
;
284 static_condvar_start
= new_static_condvar
;
287 vlc_mutex_unlock (&super_mutex
);
290 static void vlc_static_cond_destroy_all (void)
292 vlc_static_cond_t
*static_condvar
;
293 vlc_static_cond_t
*static_condvar_next
;
296 for (static_condvar
= static_condvar_start
; static_condvar
;
297 static_condvar
= static_condvar_next
)
299 static_condvar_next
= static_condvar
->next
;
301 vlc_cond_destroy (&static_condvar
->condvar
);
302 free (static_condvar
);
306 void vlc_cond_init (vlc_cond_t
*p_condvar
)
308 if (DosCreateEventSem (NULL
, &p_condvar
->hev
, 0, FALSE
) ||
309 DosCreateEventSem (NULL
, &p_condvar
->hevAck
, 0, FALSE
))
312 p_condvar
->waiters
= 0;
313 p_condvar
->signaled
= 0;
316 void vlc_cond_init_daytime (vlc_cond_t
*p_condvar
)
318 vlc_cond_init (p_condvar
);
321 void vlc_cond_destroy (vlc_cond_t
*p_condvar
)
323 DosCloseEventSem( p_condvar
->hev
);
324 DosCloseEventSem( p_condvar
->hevAck
);
327 void vlc_cond_signal (vlc_cond_t
*p_condvar
)
329 if (p_condvar
->hev
== NULLHANDLE
)
330 vlc_static_cond_init (p_condvar
);
332 if (!__atomic_cmpxchg32 (&p_condvar
->waiters
, 0, 0))
336 __atomic_xchg (&p_condvar
->signaled
, 1);
337 DosPostEventSem (p_condvar
->hev
);
339 DosWaitEventSem (p_condvar
->hevAck
, SEM_INDEFINITE_WAIT
);
340 DosResetEventSem (p_condvar
->hevAck
, &ulPost
);
344 void vlc_cond_broadcast (vlc_cond_t
*p_condvar
)
346 if (p_condvar
->hev
== NULLHANDLE
)
347 vlc_static_cond_init (p_condvar
);
349 while (!__atomic_cmpxchg32 (&p_condvar
->waiters
, 0, 0))
350 vlc_cond_signal (p_condvar
);
353 static int vlc_cond_wait_common (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
,
359 assert(p_condvar
->hev
!= NULLHANDLE
);
365 __atomic_increment (&p_condvar
->waiters
);
367 vlc_mutex_unlock (p_mutex
);
371 rc
= vlc_WaitForSingleObject( p_condvar
->hev
, ulTimeout
);
373 DosResetEventSem (p_condvar
->hev
, &ulPost
);
374 } while (rc
== NO_ERROR
&&
375 __atomic_cmpxchg32 (&p_condvar
->signaled
, 0, 1) == 0);
377 __atomic_decrement (&p_condvar
->waiters
);
379 DosPostEventSem (p_condvar
->hevAck
);
381 vlc_mutex_lock (p_mutex
);
382 } while( rc
== ERROR_INTERRUPT
);
384 return rc
? ETIMEDOUT
: 0;
387 void vlc_cond_wait (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
)
389 if (p_condvar
->hev
== NULLHANDLE
)
390 vlc_static_cond_init (p_condvar
);
392 vlc_cond_wait_common (p_condvar
, p_mutex
, SEM_INDEFINITE_WAIT
);
395 int vlc_cond_timedwait (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
,
400 mtime_t total
= mdate();
401 total
= (deadline
- total
) / 1000;
405 ulTimeout
= ( total
> 0x7fffffff ) ? 0x7fffffff : total
;
407 return vlc_cond_wait_common (p_condvar
, p_mutex
, ulTimeout
);
410 int vlc_cond_timedwait_daytime (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
,
417 gettimeofday (&tv
, NULL
);
419 total
= CLOCK_FREQ
* tv
.tv_sec
+
420 CLOCK_FREQ
* tv
.tv_usec
/ 1000000L;
421 total
= (deadline
- total
) / 1000;
425 ulTimeout
= ( total
> 0x7fffffff ) ? 0x7fffffff : total
;
427 return vlc_cond_wait_common (p_condvar
, p_mutex
, ulTimeout
);
430 /*** Thread-specific variables (TLS) ***/
434 void (*destroy
) (void *);
435 struct vlc_threadvar
*prev
;
436 struct vlc_threadvar
*next
;
437 } *vlc_threadvar_last
= NULL
;
439 int vlc_threadvar_create (vlc_threadvar_t
*p_tls
, void (*destr
) (void *))
443 struct vlc_threadvar
*var
= malloc (sizeof (*var
));
444 if (unlikely(var
== NULL
))
447 rc
= DosAllocThreadLocalMemory( 1, &var
->id
);
454 var
->destroy
= destr
;
458 vlc_mutex_lock (&super_mutex
);
459 var
->prev
= vlc_threadvar_last
;
461 var
->prev
->next
= var
;
463 vlc_threadvar_last
= var
;
464 vlc_mutex_unlock (&super_mutex
);
468 void vlc_threadvar_delete (vlc_threadvar_t
*p_tls
)
470 struct vlc_threadvar
*var
= *p_tls
;
472 vlc_mutex_lock (&super_mutex
);
473 if (var
->prev
!= NULL
)
474 var
->prev
->next
= var
->next
;
476 if (var
->next
!= NULL
)
477 var
->next
->prev
= var
->prev
;
479 vlc_threadvar_last
= var
->prev
;
481 vlc_mutex_unlock (&super_mutex
);
483 DosFreeThreadLocalMemory( var
->id
);
487 int vlc_threadvar_set (vlc_threadvar_t key
, void *value
)
489 *key
->id
= ( ULONG
)value
;
493 void *vlc_threadvar_get (vlc_threadvar_t key
)
495 return ( void * )*key
->id
;
500 void vlc_threads_setup (libvlc_int_t
*p_libvlc
)
505 static void vlc_thread_cleanup (struct vlc_thread
*th
)
510 /* TODO: use RW lock or something similar */
511 vlc_mutex_lock (&super_mutex
);
512 for (key
= vlc_threadvar_last
; key
!= NULL
; key
= key
->prev
)
514 void *value
= vlc_threadvar_get (key
);
515 if (value
!= NULL
&& key
->destroy
!= NULL
)
517 vlc_mutex_unlock (&super_mutex
);
518 vlc_threadvar_set (key
, NULL
);
519 key
->destroy (value
);
523 vlc_mutex_unlock (&super_mutex
);
527 DosCloseEventSem (th
->cancel_event
);
528 DosCloseEventSem (th
->done_event
);
530 soclose (th
->cancel_sock
);
536 static void vlc_entry( void *p
)
538 struct vlc_thread
*th
= p
;
540 vlc_threadvar_set (thread_key
, th
);
542 th
->data
= th
->entry (th
->data
);
543 DosPostEventSem( th
->done_event
);
544 vlc_thread_cleanup (th
);
547 static int vlc_clone_attr (vlc_thread_t
*p_handle
, bool detached
,
548 void *(*entry
) (void *), void *data
, int priority
)
550 struct vlc_thread
*th
= malloc (sizeof (*th
));
551 if (unlikely(th
== NULL
))
555 th
->detached
= detached
;
556 th
->killable
= false; /* not until vlc_entry() ! */
560 if( DosCreateEventSem (NULL
, &th
->cancel_event
, 0, FALSE
))
562 if( DosCreateEventSem (NULL
, &th
->done_event
, 0, FALSE
))
565 th
->cancel_sock
= socket (AF_LOCAL
, SOCK_STREAM
, 0);
566 if( th
->cancel_sock
< 0 )
569 th
->tid
= _beginthread (vlc_entry
, NULL
, 1024 * 1024, th
);
570 if((int)th
->tid
== -1)
573 if (p_handle
!= NULL
)
577 DosSetPriority(PRTYS_THREAD
,
585 soclose (th
->cancel_sock
);
586 DosCloseEventSem (th
->cancel_event
);
587 DosCloseEventSem (th
->done_event
);
593 int vlc_clone (vlc_thread_t
*p_handle
, void *(*entry
) (void *),
594 void *data
, int priority
)
596 return vlc_clone_attr (p_handle
, false, entry
, data
, priority
);
599 void vlc_join (vlc_thread_t th
, void **result
)
606 rc
= vlc_WaitForSingleObject( th
->done_event
, SEM_INDEFINITE_WAIT
);
607 } while( rc
== ERROR_INTERRUPT
);
612 DosCloseEventSem( th
->cancel_event
);
613 DosCloseEventSem( th
->done_event
);
615 soclose( th
->cancel_sock
);
620 int vlc_clone_detach (vlc_thread_t
*p_handle
, void *(*entry
) (void *),
621 void *data
, int priority
)
624 if (p_handle
== NULL
)
627 return vlc_clone_attr (p_handle
, true, entry
, data
, priority
);
630 int vlc_set_priority (vlc_thread_t th
, int priority
)
632 if (DosSetPriority(PRTYS_THREAD
,
640 vlc_thread_t
vlc_thread_self (void)
642 return vlc_threadvar_get (thread_key
);
645 unsigned long vlc_thread_id (void)
650 /*** Thread cancellation ***/
652 /* APC procedure for thread cancellation */
653 static void vlc_cancel_self (PVOID self
)
655 struct vlc_thread
*th
= self
;
657 if (likely(th
!= NULL
))
661 void vlc_cancel (vlc_thread_t thread_id
)
663 DosPostEventSem( thread_id
->cancel_event
);
664 so_cancel( thread_id
->cancel_sock
);
667 int vlc_savecancel (void)
671 struct vlc_thread
*th
= vlc_thread_self ();
673 return false; /* Main thread - cannot be cancelled anyway */
675 state
= th
->killable
;
676 th
->killable
= false;
680 void vlc_restorecancel (int state
)
682 struct vlc_thread
*th
= vlc_thread_self ();
683 assert (state
== false || state
== true);
686 return; /* Main thread - cannot be cancelled anyway */
688 assert (!th
->killable
);
689 th
->killable
= state
!= 0;
692 void vlc_testcancel (void)
694 struct vlc_thread
*th
= vlc_thread_self ();
696 return; /* Main thread - cannot be cancelled anyway */
698 /* This check is needed for the case that vlc_cancel() is followed by
699 * vlc_testcancel() without any cancellation point */
700 if( DosWaitEventSem( th
->cancel_event
, 0 ) == NO_ERROR
)
701 vlc_cancel_self( th
);
703 if (th
->killable
&& th
->killed
)
705 for (vlc_cleanup_t
*p
= th
->cleaners
; p
!= NULL
; p
= p
->next
)
708 DosPostEventSem( th
->done_event
);
709 th
->data
= NULL
; /* TODO: special value? */
710 vlc_thread_cleanup (th
);
715 void vlc_control_cancel (int cmd
, ...)
717 /* NOTE: This function only modifies thread-specific data, so there is no
718 * need to lock anything. */
721 struct vlc_thread
*th
= vlc_thread_self ();
723 return; /* Main thread - cannot be cancelled anyway */
728 case VLC_CLEANUP_PUSH
:
730 /* cleaner is a pointer to the caller stack, no need to allocate
731 * and copy anything. As a nice side effect, this cannot fail. */
732 vlc_cleanup_t
*cleaner
= va_arg (ap
, vlc_cleanup_t
*);
733 cleaner
->next
= th
->cleaners
;
734 th
->cleaners
= cleaner
;
738 case VLC_CLEANUP_POP
:
740 th
->cleaners
= th
->cleaners
->next
;
747 static int vlc_select( int nfds
, fd_set
*rdset
, fd_set
*wrset
, fd_set
*exset
,
748 struct timeval
*timeout
)
750 struct vlc_thread
*th
= vlc_thread_self( );
756 FD_SET( th
->cancel_sock
, rdset
);
758 nfds
= MAX( nfds
, th
->cancel_sock
+ 1 );
761 rc
= select( nfds
, rdset
, wrset
, exset
, timeout
);
769 /* Export vlc_poll_os2 directly regardless of EXPORTS of .def */
770 __declspec(dllexport
)
771 int vlc_poll_os2( struct pollfd
*fds
, unsigned nfds
, int timeout
);
773 __declspec(dllexport
)
774 int vlc_poll_os2( struct pollfd
*fds
, unsigned nfds
, int timeout
)
776 fd_set rdset
, wrset
, exset
;
780 struct timeval tv
= { 0, 0 };
787 for( unsigned i
= 0; i
< nfds
; i
++ )
789 int fd
= fds
[ i
].fd
;
792 fds
[ i
].revents
= 0;
794 if( fstat( fd
, &stbuf
) == -1 ||
795 (errno
= 0, !S_ISSOCK( stbuf
.st_mode
)))
799 /* If regular files, assume readiness for requested modes */
800 fds
[ i
].revents
= ( !errno
&& S_ISREG( stbuf
.st_mode
))
801 ? ( fds
[ i
].events
&
802 ( POLLIN
| POLLOUT
| POLLPRI
))
814 if(( unsigned )fd
>= FD_SETSIZE
)
820 if( fds
[ i
].events
& POLLIN
)
821 FD_SET( fd
, &rdset
);
822 if( fds
[ i
].events
& POLLOUT
)
823 FD_SET( fd
, &wrset
);
824 if( fds
[ i
].events
& POLLPRI
)
825 FD_SET( fd
, &exset
);
828 if( non_sockets
> 0 )
829 timeout
= 0; /* Just check pending sockets */
831 /* Sockets included ? */
834 struct timeval
*ptv
= NULL
;
838 div_t d
= div( timeout
, 1000 );
840 tv
.tv_usec
= d
.rem
* 1000;
845 if (vlc_select( val
+ 1, &rdset
, &wrset
, &exset
, ptv
) == -1)
850 for( unsigned i
= 0; i
< nfds
; i
++ )
852 int fd
= fds
[ i
].fd
;
854 if( fd
>= 0 && fds
[ i
].revents
== 0 )
856 fds
[ i
].revents
= ( FD_ISSET( fd
, &rdset
) ? POLLIN
: 0 )
857 | ( FD_ISSET( fd
, &wrset
) ? POLLOUT
: 0 )
858 | ( FD_ISSET( fd
, &exset
) ? POLLPRI
: 0 );
861 if( fds
[ i
].revents
!= 0 )
868 #define Q2LL( q ) ( *( long long * )&( q ))
873 /* We don't need the real date, just the value of a high precision timer */
876 if (DosTmrQueryTime(&counter
) || DosTmrQueryFreq(&freq
))
879 /* Convert to from (1/freq) to microsecond resolution */
880 /* We need to split the division to avoid 63-bits overflow */
881 lldiv_t d
= lldiv (Q2LL(counter
), freq
);
883 return (d
.quot
* 1000000) + ((d
.rem
* 1000000) / freq
);
887 void mwait (mtime_t deadline
)
892 while ((delay
= (deadline
- mdate())) > 0)
895 if (unlikely(delay
> 0x7fffffff))
903 void msleep (mtime_t delay
)
905 mwait (mdate () + delay
);
916 void (*func
) (void *);
920 static void vlc_timer_do (void *arg
)
922 struct vlc_timer
*timer
= arg
;
928 DosWaitEventSem (timer
->hev
, SEM_INDEFINITE_WAIT
);
929 DosResetEventSem (timer
->hev
, &count
);
934 timer
->func (timer
->data
);
937 DosAsyncTimer (timer
->interval
, (HSEM
)timer
->hev
, &timer
->htimer
);
941 int vlc_timer_create (vlc_timer_t
*id
, void (*func
) (void *), void *data
)
943 struct vlc_timer
*timer
= malloc (sizeof (*timer
));
951 DosCreateEventSem (NULL
, &timer
->hev
, DC_SEM_SHARED
, FALSE
);
952 timer
->htimer
= NULLHANDLE
;
955 timer
->tid
= _beginthread (vlc_timer_do
, NULL
, 1024 * 1024, timer
);
961 void vlc_timer_destroy (vlc_timer_t timer
)
963 if (timer
->htimer
!= NULLHANDLE
)
964 DosStopTimer (timer
->htimer
);
967 DosPostEventSem (timer
->hev
);
968 DosWaitThread (&timer
->tid
, DCWW_WAIT
);
969 DosCloseEventSem (timer
->hev
);
974 void vlc_timer_schedule (vlc_timer_t timer
, bool absolute
,
975 mtime_t value
, mtime_t interval
)
977 if (timer
->htimer
!= NULLHANDLE
)
979 DosStopTimer (timer
->htimer
);
980 timer
->htimer
= NULLHANDLE
;
989 value
= (value
+ 999) / 1000;
990 interval
= (interval
+ 999) / 1000;
992 timer
->interval
= interval
;
993 if (DosAsyncTimer (value
, (HSEM
)timer
->hev
, &timer
->htimer
))
997 unsigned vlc_timer_getoverrun (vlc_timer_t timer
)
1004 unsigned vlc_GetCPUCount (void)
1008 DosQuerySysInfo(QSV_NUMPROCESSORS
, QSV_NUMPROCESSORS
,
1009 &numprocs
, sizeof(numprocs
));