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
);
160 case 1 : /* Termination */
161 vlc_rwlock_destroy (&config_lock
);
162 vlc_threadvar_delete (&thread_key
);
163 vlc_cond_destroy (&super_variable
);
164 vlc_mutex_destroy (&super_mutex
);
165 vlc_static_cond_destroy_all ();
172 return 0; /* Failed */
176 void vlc_mutex_init( vlc_mutex_t
*p_mutex
)
178 /* This creates a recursive mutex. This is OK as fast mutexes have
179 * no defined behavior in case of recursive locking. */
180 DosCreateMutexSem( NULL
, &p_mutex
->hmtx
, 0, FALSE
);
181 p_mutex
->dynamic
= true;
184 void vlc_mutex_init_recursive( vlc_mutex_t
*p_mutex
)
186 DosCreateMutexSem( NULL
, &p_mutex
->hmtx
, 0, FALSE
);
187 p_mutex
->dynamic
= true;
191 void vlc_mutex_destroy (vlc_mutex_t
*p_mutex
)
193 assert (p_mutex
->dynamic
);
194 DosCloseMutexSem( p_mutex
->hmtx
);
197 void vlc_mutex_lock (vlc_mutex_t
*p_mutex
)
199 if (!p_mutex
->dynamic
)
200 { /* static mutexes */
201 int canc
= vlc_savecancel ();
202 assert (p_mutex
!= &super_mutex
); /* this one cannot be static */
204 vlc_mutex_lock (&super_mutex
);
205 while (p_mutex
->locked
)
207 p_mutex
->contention
++;
208 vlc_cond_wait (&super_variable
, &super_mutex
);
209 p_mutex
->contention
--;
211 p_mutex
->locked
= true;
212 vlc_mutex_unlock (&super_mutex
);
213 vlc_restorecancel (canc
);
217 DosRequestMutexSem(p_mutex
->hmtx
, SEM_INDEFINITE_WAIT
);
220 int vlc_mutex_trylock (vlc_mutex_t
*p_mutex
)
222 if (!p_mutex
->dynamic
)
223 { /* static mutexes */
226 assert (p_mutex
!= &super_mutex
); /* this one cannot be static */
227 vlc_mutex_lock (&super_mutex
);
228 if (!p_mutex
->locked
)
230 p_mutex
->locked
= true;
233 vlc_mutex_unlock (&super_mutex
);
237 return DosRequestMutexSem( p_mutex
->hmtx
, 0 ) ? EBUSY
: 0;
240 void vlc_mutex_unlock (vlc_mutex_t
*p_mutex
)
242 if (!p_mutex
->dynamic
)
243 { /* static mutexes */
244 assert (p_mutex
!= &super_mutex
); /* this one cannot be static */
246 vlc_mutex_lock (&super_mutex
);
247 assert (p_mutex
->locked
);
248 p_mutex
->locked
= false;
249 if (p_mutex
->contention
)
250 vlc_cond_broadcast (&super_variable
);
251 vlc_mutex_unlock (&super_mutex
);
255 DosReleaseMutexSem( p_mutex
->hmtx
);
258 /*** Condition variables ***/
259 typedef struct vlc_static_cond_t vlc_static_cond_t
;
261 struct vlc_static_cond_t
264 vlc_static_cond_t
*next
;
267 static vlc_static_cond_t
*static_condvar_start
= NULL
;
269 static void vlc_static_cond_init (vlc_cond_t
*p_condvar
)
271 vlc_mutex_lock (&super_mutex
);
273 if (p_condvar
->hev
== NULLHANDLE
)
275 vlc_cond_init (p_condvar
);
277 vlc_static_cond_t
*new_static_condvar
;
279 new_static_condvar
= malloc (sizeof (*new_static_condvar
));
280 if (unlikely (!new_static_condvar
))
283 memcpy (&new_static_condvar
->condvar
, p_condvar
, sizeof (*p_condvar
));
284 new_static_condvar
->next
= static_condvar_start
;
285 static_condvar_start
= new_static_condvar
;
288 vlc_mutex_unlock (&super_mutex
);
291 static void vlc_static_cond_destroy_all (void)
293 vlc_static_cond_t
*static_condvar
;
294 vlc_static_cond_t
*static_condvar_next
;
297 for (static_condvar
= static_condvar_start
; static_condvar
;
298 static_condvar
= static_condvar_next
)
300 static_condvar_next
= static_condvar
->next
;
302 vlc_cond_destroy (&static_condvar
->condvar
);
303 free (static_condvar
);
307 void vlc_cond_init (vlc_cond_t
*p_condvar
)
309 if (DosCreateEventSem (NULL
, &p_condvar
->hev
, 0, FALSE
) ||
310 DosCreateEventSem (NULL
, &p_condvar
->hevAck
, 0, FALSE
))
313 p_condvar
->waiters
= 0;
314 p_condvar
->signaled
= 0;
317 void vlc_cond_init_daytime (vlc_cond_t
*p_condvar
)
319 vlc_cond_init (p_condvar
);
322 void vlc_cond_destroy (vlc_cond_t
*p_condvar
)
324 DosCloseEventSem( p_condvar
->hev
);
325 DosCloseEventSem( p_condvar
->hevAck
);
328 void vlc_cond_signal (vlc_cond_t
*p_condvar
)
330 if (p_condvar
->hev
== NULLHANDLE
)
331 vlc_static_cond_init (p_condvar
);
333 if (!__atomic_cmpxchg32 (&p_condvar
->waiters
, 0, 0))
337 __atomic_xchg (&p_condvar
->signaled
, 1);
338 DosPostEventSem (p_condvar
->hev
);
340 DosWaitEventSem (p_condvar
->hevAck
, SEM_INDEFINITE_WAIT
);
341 DosResetEventSem (p_condvar
->hevAck
, &ulPost
);
345 void vlc_cond_broadcast (vlc_cond_t
*p_condvar
)
347 if (p_condvar
->hev
== NULLHANDLE
)
348 vlc_static_cond_init (p_condvar
);
350 while (!__atomic_cmpxchg32 (&p_condvar
->waiters
, 0, 0))
351 vlc_cond_signal (p_condvar
);
354 static int vlc_cond_wait_common (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
,
360 assert(p_condvar
->hev
!= NULLHANDLE
);
366 __atomic_increment (&p_condvar
->waiters
);
368 vlc_mutex_unlock (p_mutex
);
372 rc
= vlc_WaitForSingleObject( p_condvar
->hev
, ulTimeout
);
374 DosResetEventSem (p_condvar
->hev
, &ulPost
);
375 } while (rc
== NO_ERROR
&&
376 __atomic_cmpxchg32 (&p_condvar
->signaled
, 0, 1) == 0);
378 __atomic_decrement (&p_condvar
->waiters
);
380 DosPostEventSem (p_condvar
->hevAck
);
382 vlc_mutex_lock (p_mutex
);
383 } while( rc
== ERROR_INTERRUPT
);
385 return rc
? ETIMEDOUT
: 0;
388 void vlc_cond_wait (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
)
390 if (p_condvar
->hev
== NULLHANDLE
)
391 vlc_static_cond_init (p_condvar
);
393 vlc_cond_wait_common (p_condvar
, p_mutex
, SEM_INDEFINITE_WAIT
);
396 int vlc_cond_timedwait (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
,
401 mtime_t total
= mdate();
402 total
= (deadline
- total
) / 1000;
406 ulTimeout
= ( total
> 0x7fffffff ) ? 0x7fffffff : total
;
408 return vlc_cond_wait_common (p_condvar
, p_mutex
, ulTimeout
);
411 int vlc_cond_timedwait_daytime (vlc_cond_t
*p_condvar
, vlc_mutex_t
*p_mutex
,
418 gettimeofday (&tv
, NULL
);
420 total
= CLOCK_FREQ
* tv
.tv_sec
+
421 CLOCK_FREQ
* tv
.tv_usec
/ 1000000L;
422 total
= (deadline
- total
) / 1000;
426 ulTimeout
= ( total
> 0x7fffffff ) ? 0x7fffffff : total
;
428 return vlc_cond_wait_common (p_condvar
, p_mutex
, ulTimeout
);
431 /*** Thread-specific variables (TLS) ***/
435 void (*destroy
) (void *);
436 struct vlc_threadvar
*prev
;
437 struct vlc_threadvar
*next
;
438 } *vlc_threadvar_last
= NULL
;
440 int vlc_threadvar_create (vlc_threadvar_t
*p_tls
, void (*destr
) (void *))
444 struct vlc_threadvar
*var
= malloc (sizeof (*var
));
445 if (unlikely(var
== NULL
))
448 rc
= DosAllocThreadLocalMemory( 1, &var
->id
);
455 var
->destroy
= destr
;
459 vlc_mutex_lock (&super_mutex
);
460 var
->prev
= vlc_threadvar_last
;
462 var
->prev
->next
= var
;
464 vlc_threadvar_last
= var
;
465 vlc_mutex_unlock (&super_mutex
);
469 void vlc_threadvar_delete (vlc_threadvar_t
*p_tls
)
471 struct vlc_threadvar
*var
= *p_tls
;
473 vlc_mutex_lock (&super_mutex
);
474 if (var
->prev
!= NULL
)
475 var
->prev
->next
= var
->next
;
477 if (var
->next
!= NULL
)
478 var
->next
->prev
= var
->prev
;
480 vlc_threadvar_last
= var
->prev
;
482 vlc_mutex_unlock (&super_mutex
);
484 DosFreeThreadLocalMemory( var
->id
);
488 int vlc_threadvar_set (vlc_threadvar_t key
, void *value
)
490 *key
->id
= ( ULONG
)value
;
494 void *vlc_threadvar_get (vlc_threadvar_t key
)
496 return ( void * )*key
->id
;
501 void vlc_threads_setup (libvlc_int_t
*p_libvlc
)
506 static void vlc_thread_cleanup (struct vlc_thread
*th
)
511 /* TODO: use RW lock or something similar */
512 vlc_mutex_lock (&super_mutex
);
513 for (key
= vlc_threadvar_last
; key
!= NULL
; key
= key
->prev
)
515 void *value
= vlc_threadvar_get (key
);
516 if (value
!= NULL
&& key
->destroy
!= NULL
)
518 vlc_mutex_unlock (&super_mutex
);
519 vlc_threadvar_set (key
, NULL
);
520 key
->destroy (value
);
524 vlc_mutex_unlock (&super_mutex
);
528 DosCloseEventSem (th
->cancel_event
);
529 DosCloseEventSem (th
->done_event
);
531 soclose (th
->cancel_sock
);
537 static void vlc_entry( void *p
)
539 struct vlc_thread
*th
= p
;
541 vlc_threadvar_set (thread_key
, th
);
543 th
->data
= th
->entry (th
->data
);
544 DosPostEventSem( th
->done_event
);
545 vlc_thread_cleanup (th
);
548 static int vlc_clone_attr (vlc_thread_t
*p_handle
, bool detached
,
549 void *(*entry
) (void *), void *data
, int priority
)
551 struct vlc_thread
*th
= malloc (sizeof (*th
));
552 if (unlikely(th
== NULL
))
556 th
->detached
= detached
;
557 th
->killable
= false; /* not until vlc_entry() ! */
561 if( DosCreateEventSem (NULL
, &th
->cancel_event
, 0, FALSE
))
563 if( DosCreateEventSem (NULL
, &th
->done_event
, 0, FALSE
))
566 th
->cancel_sock
= socket (AF_LOCAL
, SOCK_STREAM
, 0);
567 if( th
->cancel_sock
< 0 )
570 th
->tid
= _beginthread (vlc_entry
, NULL
, 1024 * 1024, th
);
571 if((int)th
->tid
== -1)
574 if (p_handle
!= NULL
)
578 DosSetPriority(PRTYS_THREAD
,
586 soclose (th
->cancel_sock
);
587 DosCloseEventSem (th
->cancel_event
);
588 DosCloseEventSem (th
->done_event
);
594 int vlc_clone (vlc_thread_t
*p_handle
, void *(*entry
) (void *),
595 void *data
, int priority
)
597 return vlc_clone_attr (p_handle
, false, entry
, data
, priority
);
600 void vlc_join (vlc_thread_t th
, void **result
)
607 rc
= vlc_WaitForSingleObject( th
->done_event
, SEM_INDEFINITE_WAIT
);
608 } while( rc
== ERROR_INTERRUPT
);
613 DosCloseEventSem( th
->cancel_event
);
614 DosCloseEventSem( th
->done_event
);
616 soclose( th
->cancel_sock
);
621 int vlc_clone_detach (vlc_thread_t
*p_handle
, void *(*entry
) (void *),
622 void *data
, int priority
)
625 if (p_handle
== NULL
)
628 return vlc_clone_attr (p_handle
, true, entry
, data
, priority
);
631 int vlc_set_priority (vlc_thread_t th
, int priority
)
633 if (DosSetPriority(PRTYS_THREAD
,
641 vlc_thread_t
vlc_thread_self (void)
643 return vlc_threadvar_get (thread_key
);
646 unsigned long vlc_thread_id (void)
651 /*** Thread cancellation ***/
653 /* APC procedure for thread cancellation */
654 static void vlc_cancel_self (PVOID self
)
656 struct vlc_thread
*th
= self
;
658 if (likely(th
!= NULL
))
662 void vlc_cancel (vlc_thread_t thread_id
)
664 DosPostEventSem( thread_id
->cancel_event
);
665 so_cancel( thread_id
->cancel_sock
);
668 int vlc_savecancel (void)
672 struct vlc_thread
*th
= vlc_thread_self ();
674 return false; /* Main thread - cannot be cancelled anyway */
676 state
= th
->killable
;
677 th
->killable
= false;
681 void vlc_restorecancel (int state
)
683 struct vlc_thread
*th
= vlc_thread_self ();
684 assert (state
== false || state
== true);
687 return; /* Main thread - cannot be cancelled anyway */
689 assert (!th
->killable
);
690 th
->killable
= state
!= 0;
693 void vlc_testcancel (void)
695 struct vlc_thread
*th
= vlc_thread_self ();
697 return; /* Main thread - cannot be cancelled anyway */
699 /* This check is needed for the case that vlc_cancel() is followed by
700 * vlc_testcancel() without any cancellation point */
701 if( DosWaitEventSem( th
->cancel_event
, 0 ) == NO_ERROR
)
702 vlc_cancel_self( th
);
704 if (th
->killable
&& th
->killed
)
706 for (vlc_cleanup_t
*p
= th
->cleaners
; p
!= NULL
; p
= p
->next
)
709 DosPostEventSem( th
->done_event
);
710 th
->data
= NULL
; /* TODO: special value? */
711 vlc_thread_cleanup (th
);
716 void vlc_control_cancel (int cmd
, ...)
718 /* NOTE: This function only modifies thread-specific data, so there is no
719 * need to lock anything. */
722 struct vlc_thread
*th
= vlc_thread_self ();
724 return; /* Main thread - cannot be cancelled anyway */
729 case VLC_CLEANUP_PUSH
:
731 /* cleaner is a pointer to the caller stack, no need to allocate
732 * and copy anything. As a nice side effect, this cannot fail. */
733 vlc_cleanup_t
*cleaner
= va_arg (ap
, vlc_cleanup_t
*);
734 cleaner
->next
= th
->cleaners
;
735 th
->cleaners
= cleaner
;
739 case VLC_CLEANUP_POP
:
741 th
->cleaners
= th
->cleaners
->next
;
748 static int vlc_select( int nfds
, fd_set
*rdset
, fd_set
*wrset
, fd_set
*exset
,
749 struct timeval
*timeout
)
751 struct vlc_thread
*th
= vlc_thread_self( );
757 FD_SET( th
->cancel_sock
, rdset
);
759 nfds
= MAX( nfds
, th
->cancel_sock
+ 1 );
762 rc
= select( nfds
, rdset
, wrset
, exset
, timeout
);
770 /* Export vlc_poll_os2 directly regardless of EXPORTS of .def */
771 __declspec(dllexport
)
772 int vlc_poll_os2( struct pollfd
*fds
, unsigned nfds
, int timeout
);
774 __declspec(dllexport
)
775 int vlc_poll_os2( struct pollfd
*fds
, unsigned nfds
, int timeout
)
777 fd_set rdset
, wrset
, exset
;
781 struct timeval tv
= { 0, 0 };
788 for( unsigned i
= 0; i
< nfds
; i
++ )
790 int fd
= fds
[ i
].fd
;
793 fds
[ i
].revents
= 0;
795 if( fstat( fd
, &stbuf
) == -1 ||
796 (errno
= 0, !S_ISSOCK( stbuf
.st_mode
)))
800 /* If regular files, assume readiness for requested modes */
801 fds
[ i
].revents
= ( !errno
&& S_ISREG( stbuf
.st_mode
))
802 ? ( fds
[ i
].events
&
803 ( POLLIN
| POLLOUT
| POLLPRI
))
815 if(( unsigned )fd
>= FD_SETSIZE
)
821 if( fds
[ i
].events
& POLLIN
)
822 FD_SET( fd
, &rdset
);
823 if( fds
[ i
].events
& POLLOUT
)
824 FD_SET( fd
, &wrset
);
825 if( fds
[ i
].events
& POLLPRI
)
826 FD_SET( fd
, &exset
);
829 if( non_sockets
> 0 )
830 timeout
= 0; /* Just check pending sockets */
832 /* Sockets included ? */
835 struct timeval
*ptv
= NULL
;
839 div_t d
= div( timeout
, 1000 );
841 tv
.tv_usec
= d
.rem
* 1000;
846 if (vlc_select( val
+ 1, &rdset
, &wrset
, &exset
, ptv
) == -1)
851 for( unsigned i
= 0; i
< nfds
; i
++ )
853 int fd
= fds
[ i
].fd
;
855 if( fd
>= 0 && fds
[ i
].revents
== 0 )
857 fds
[ i
].revents
= ( FD_ISSET( fd
, &rdset
) ? POLLIN
: 0 )
858 | ( FD_ISSET( fd
, &wrset
) ? POLLOUT
: 0 )
859 | ( FD_ISSET( fd
, &exset
) ? POLLPRI
: 0 );
862 if( fds
[ i
].revents
!= 0 )
869 #define Q2LL( q ) ( *( long long * )&( q ))
874 /* We don't need the real date, just the value of a high precision timer */
877 if (DosTmrQueryTime(&counter
) || DosTmrQueryFreq(&freq
))
880 /* Convert to from (1/freq) to microsecond resolution */
881 /* We need to split the division to avoid 63-bits overflow */
882 lldiv_t d
= lldiv (Q2LL(counter
), freq
);
884 return (d
.quot
* 1000000) + ((d
.rem
* 1000000) / freq
);
888 void mwait (mtime_t deadline
)
893 while ((delay
= (deadline
- mdate())) > 0)
896 if (unlikely(delay
> 0x7fffffff))
904 void msleep (mtime_t delay
)
906 mwait (mdate () + delay
);
917 void (*func
) (void *);
921 static void vlc_timer_do (void *arg
)
923 struct vlc_timer
*timer
= arg
;
929 DosWaitEventSem (timer
->hev
, SEM_INDEFINITE_WAIT
);
930 DosResetEventSem (timer
->hev
, &count
);
935 timer
->func (timer
->data
);
938 DosAsyncTimer (timer
->interval
, (HSEM
)timer
->hev
, &timer
->htimer
);
942 int vlc_timer_create (vlc_timer_t
*id
, void (*func
) (void *), void *data
)
944 struct vlc_timer
*timer
= malloc (sizeof (*timer
));
952 DosCreateEventSem (NULL
, &timer
->hev
, DC_SEM_SHARED
, FALSE
);
953 timer
->htimer
= NULLHANDLE
;
956 timer
->tid
= _beginthread (vlc_timer_do
, NULL
, 1024 * 1024, timer
);
962 void vlc_timer_destroy (vlc_timer_t timer
)
964 if (timer
->htimer
!= NULLHANDLE
)
965 DosStopTimer (timer
->htimer
);
968 DosPostEventSem (timer
->hev
);
969 DosWaitThread (&timer
->tid
, DCWW_WAIT
);
970 DosCloseEventSem (timer
->hev
);
975 void vlc_timer_schedule (vlc_timer_t timer
, bool absolute
,
976 mtime_t value
, mtime_t interval
)
978 if (timer
->htimer
!= NULLHANDLE
)
980 DosStopTimer (timer
->htimer
);
981 timer
->htimer
= NULLHANDLE
;
990 value
= (value
+ 999) / 1000;
991 interval
= (interval
+ 999) / 1000;
993 timer
->interval
= interval
;
994 if (DosAsyncTimer (value
, (HSEM
)timer
->hev
, &timer
->htimer
))
998 unsigned vlc_timer_getoverrun (vlc_timer_t timer
)
1005 unsigned vlc_GetCPUCount (void)
1009 DosQuerySysInfo(QSV_NUMPROCESSORS
, QSV_NUMPROCESSORS
,
1010 &numprocs
, sizeof(numprocs
));