]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/plr/lib/libpthread_romain/src/mutex.c
42fcf9e0ede752637609f8be391ee52517c8f829
[l4.git] / l4 / pkg / plr / lib / libpthread_romain / src / mutex.c
1 /* Linuxthreads - a simple clone()-based implementation of Posix        */
2 /* threads for Linux.                                                   */
3 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
4 /*                                                                      */
5 /* This program is free software; you can redistribute it and/or        */
6 /* modify it under the terms of the GNU Library General Public License  */
7 /* as published by the Free Software Foundation; either version 2       */
8 /* of the License, or (at your option) any later version.               */
9 /*                                                                      */
10 /* This program is distributed in the hope that it will be useful,      */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
13 /* GNU Library General Public License for more details.                 */
14
15 /* Mutexes */
16
17 #include "descr.h"
18 #include <bits/libc-lock.h>
19 #include <errno.h>
20 #ifdef NOT_FOR_L4
21 #include <sched.h>
22 #endif
23 #include <stddef.h>
24 #include <limits.h>
25 #include "pthread.h"
26 #include "internals.h"
27 #include "spinlock.h"
28 #include "queue.h"
29 #include "restart.h"
30
31 #include <l4/util/atomic.h>
32 #include <l4/plr/pthread_rep.h>
33
34
35 int pthread_mutex_lock_rep(pthread_mutex_t * mutex);
36 int pthread_mutex_unlock_rep(pthread_mutex_t * mutex);
37
38
39 #define LOCKli(li, mtx) (li)->locks[(mtx)->__m_reserved]
40 #define ACQ(li, mtx)    lock_li(  (li), (mtx)->__m_reserved)
41 #define REL(li, mtx)    unlock_li((li), (mtx)->__m_reserved)
42
43 #define YIELD()  yield() 
44 #define BARRIER() asm volatile ("" : : : "memory");
45 #define ASSERT42(cond, msg) do { /*if (cond) enter_kdebug42(msg);*/ } while (0)
46
47 #define GO_TO_SLEEP 0
48
49 /*
50  * The generated code uses registers to access and modify data in
51  * the lock info page. This page is shared between all replicas, but
52  * the counts written to it may differ between replicas, which in turn
53  * may lead to the master process detecting state deviation if the values
54  * remain in those registers.
55  *
56  * To fix that, we store the original values of EBX, ECX, and EDX in
57  * thread-private storage and restore them before we a) leave the function or
58  * b) perform a system call that would be observed by the master.
59  *
60  * XXX: This assumes that we will not cause a page fault or
61  *      any other exception during execution, because then we might end
62  *      up with differing register values as well.
63  */
64 static inline void rep_function_save_regs(void)
65 {
66   BARRIER();
67   asm volatile ("mov %%ebx, %0\t\n"
68                 "mov %%ecx, %1\t\n"
69                 "mov %%edx, %2\t\n"
70                 /*
71                 "mov %%esi, %3\t\n"
72                 "mov %%edi, %4\t\n"*/
73                 : "=m" (thread_self()->ebx),
74                   "=m" (thread_self()->ecx),
75                   "=m" (thread_self()->edx)/*,
76                   "=m" (thread_self()->esi),
77                   "=m" (thread_self()->edi)*/
78                 :
79                 : "memory"
80   );
81 }
82
83
84 static inline void rep_function_restore_regs(void)
85 {
86   
87   BARRIER();
88   asm volatile ("mov %0, %%ebx\t\n"
89                 "mov %1, %%ecx\t\n"
90                 "mov %2, %%edx\t\n"
91                 /*
92                 "mov %3, %%esi\t\n"
93                 "mov %4, %%edi\t\n"*/
94                 :
95                 : "m" (thread_self()->ebx),
96                   "m" (thread_self()->ecx),
97                   "m" (thread_self()->edx)/*,
98                   "m" (thread_self()->esi),
99                   "m" (thread_self()->edi)*/
100                 : "memory"
101   );
102 }
103
104 static inline int yield()
105 {
106         rep_function_restore_regs();
107         asm volatile ("ud2" : : : "edx", "ecx", "ebx", "memory");
108         rep_function_save_regs();
109 }
110
111
112 static inline void lock_rep_wait(pthread_mutex_t* mutex)
113 {
114     /*
115      * Go to sleep. This is a system call and will be checked by the master. Therefore,
116      * we need to load the ECX and EDX values we pushed in the beginning, so that the
117      * master process sees a consistent state here.
118      */
119     rep_function_restore_regs();
120     asm volatile (
121                   "push %0\t\n"
122                   "mov $0xA020, %%eax\t\n"
123                   "call *%%eax\t\n"
124                   "pop %0\t\n"
125                   :
126                   : "r" (mutex)
127                   : "eax", "memory");
128     rep_function_save_regs();
129 }
130
131
132
133 static inline void lock_rep_post(pthread_mutex_t* mutex)
134 {
135     /*
136      * Send the actual notification. This is a special case in the master,
137      * because here only one replica performs the system call while all
138      * others continue untouched.
139      */
140     BARRIER();
141     asm volatile ("push %0\t\n"
142                   "mov $0xA040, %%eax\t\n"
143                   "call *%%eax\t\n"
144                   "pop %0\t\n": : "r" (mutex) : "eax", "memory");
145 }
146
147
148 static void init_replica_mutex(pthread_mutex_t* mtx)
149 {
150   unsigned i = 0;
151   lock_info* li = get_lock_info();
152
153   rep_function_save_regs();
154   
155   /*
156    * find either the respective lock (if it has been registered by another
157    * replica yet) or a free slot to use
158    */
159   for (i = 0; i < NUM_LOCKS; ++i) {
160
161         lock_li(li, i);
162
163         // slot already acquired by another replica
164         if (li->locks[i].lockdesc == (l4_addr_t)mtx) {
165           mtx->__m_reserved = i;
166           unlock_li(li, i);
167           break;
168         }
169
170         // free slot -> we can use it as we do not release
171         // slots yet
172         if (li->locks[i].owner == lock_entry_free) {
173           li->locks[i].lockdesc = (l4_addr_t)mtx;
174           li->locks[i].owner    = lock_unowned;
175           mtx->__m_reserved     = i;
176           unlock_li(li, i);
177           break;
178         }
179
180         unlock_li(li, i);
181   }
182
183   if (i >= NUM_LOCKS) {
184           enter_kdebug("out of locks");
185   }
186   
187   rep_function_restore_regs();
188 }
189
190 int
191 attribute_hidden
192 pthread_mutex_lock_rep(pthread_mutex_t * mutex)
193 {
194     rep_function_save_regs();
195     
196         /*
197          * not initialized yet? -> happens for statically initialized
198          * locks as those don't call mutex_init(). And as we need to
199          * handle this anyway, we simply don't overwrite mutex_init()
200          * at all.
201          */
202         if (!mutex->__m_reserved) {
203           init_replica_mutex(mutex);
204         }
205
206     unsigned retry_counter   = 0;
207     lock_info* li            = get_lock_info();
208     thread_self()->p_epoch  += 1;
209     
210     /*outstring("lock() "); outhex32(thread_self()->p_epoch); outstring("\n");*/
211  
212     while (1) {
213
214                 ACQ(li, mutex);
215     
216         if (LOCKli(li, mutex).owner == lock_unowned)
217         {
218             ASSERT42(LOCKli(li, mutex).wait_count != 0, "wait count != 0");
219             ASSERT42(LOCKli(li, mutex).acq_count != 0,  "acq count  != 0");
220             ASSERT42(LOCKli(li, mutex).wake_count != 0, "wake count != 0");
221             
222             LOCKli(li, mutex).owner       = (l4_addr_t)thread_self();
223             LOCKli(li, mutex).owner_epoch = thread_self()->p_epoch;
224             LOCKli(li, mutex).acq_count   = li->replica_count;
225             break;
226         }
227         else if (LOCKli(li, mutex).owner == (l4_addr_t)thread_self())
228         {
229             if (LOCKli(li, mutex).owner_epoch != thread_self()->p_epoch) {
230                 //outchar42('.'); outchar42(' '); outhex42(thread_self());
231                 REL(li, mutex);
232                 YIELD();
233                 continue;
234                 
235                 // XXX allow multiple subsequent lock acquisitions */
236                 /*
237                 outhex42(LOCKli(li, mutex).owner_epoch); outchar42(' ');
238                 outhex42(thread_self()->p_epoch); outchar42('\n');
239                 enter_kdebug42("epoch mismatch");
240                 */
241             }
242
243             break;
244             //enter_kdebug42("mtx owned by me");
245         }
246         else
247         {
248                 /*
249                  * XXX: Spin for a short while?
250                  */
251 #if GO_TO_SLEEP
252                         LOCKli(li, mutex).wait_count += 1;
253                         REL(li, mutex);
254                         
255                         lock_rep_wait(mutex);
256
257                         ACQ(li, mutex);
258
259                         LOCKli(li, mutex).wake_count -= 1;
260                         LOCKli(li, mutex).wait_count -= 1;
261
262                         if (LOCKli(li, mutex).wake_count == 0) {
263                                 ASSERT42(LOCKli(li, mutex).acq_count != 0,  "acq count  != 0");
264                                 LOCKli(li, mutex).owner       = (l4_addr_t)thread_self();
265                                 LOCKli(li, mutex).owner_epoch = thread_self()->p_epoch;
266                                 LOCKli(li, mutex).acq_count   = li->replica_count;
267                         }
268
269                         REL(li, mutex);
270                         break;
271
272 #else
273             REL(li, mutex);
274             YIELD();
275             continue;
276 #endif
277             //enter_kdebug42("mtx: other owner");
278         }
279     }
280     
281     REL(li, mutex);
282
283     rep_function_restore_regs();
284         return 0;
285 }
286
287
288 int pthread_mutex_unlock_rep(pthread_mutex_t * mutex);
289
290 int
291 attribute_hidden
292 pthread_mutex_unlock_rep(pthread_mutex_t * mutex)
293 {
294     rep_function_save_regs();
295     lock_info *li = get_lock_info();
296     
297 retry2:
298     ACQ(li, mutex);
299     
300     ASSERT42(LOCKli(li, mutex).owner != (l4_addr_t)thread_self(), "unlock not by owner");
301     ASSERT42(LOCKli(li, mutex).acq_count == 0, "acq count == 0");
302     
303     LOCKli(li, mutex).acq_count -= 1;
304     if (LOCKli(li, mutex).acq_count == 0) {
305 #if GO_TO_SLEEP
306         if (LOCKli(li, mutex).wait_count != 0) {
307                 LOCKli(li, mutex).wake_count = li->replica_count;
308                 lock_rep_post(mutex);
309                 /* don't reset owner */
310         } else {
311             LOCKli(li, mutex).owner = lock_unowned;
312                 }
313 #else
314         LOCKli(li, mutex).owner = lock_unowned;
315 #endif
316     }
317
318     REL(li, mutex);
319     rep_function_restore_regs();
320     
321     //if (thread_self()->p_epoch % 10 == 0)
322     //    YIELD();
323     
324         return 0;
325 }
326
327 #undef LOCKli
328
329 int
330 attribute_hidden
331 __pthread_mutex_init(pthread_mutex_t * mutex,
332                        const pthread_mutexattr_t * mutex_attr)
333 {
334   __pthread_init_lock(&mutex->__m_lock);
335   mutex->__m_kind =
336     mutex_attr == NULL ? PTHREAD_MUTEX_TIMED_NP : mutex_attr->__mutexkind;
337   mutex->__m_count = 0;
338   mutex->__m_owner = NULL;
339   return 0;
340 }
341 strong_alias (__pthread_mutex_init, pthread_mutex_init)
342
343 int
344 attribute_hidden
345 __pthread_mutex_destroy(pthread_mutex_t * mutex)
346 {
347   switch (mutex->__m_kind) {
348   case PTHREAD_MUTEX_ADAPTIVE_NP:
349   case PTHREAD_MUTEX_RECURSIVE_NP:
350     if ((mutex->__m_lock.__status & 1) != 0)
351       return EBUSY;
352     return 0;
353   case PTHREAD_MUTEX_ERRORCHECK_NP:
354   case PTHREAD_MUTEX_TIMED_NP:
355     if (mutex->__m_lock.__status != 0)
356       return EBUSY;
357     return 0;
358   default:
359     return EINVAL;
360   }
361 }
362 strong_alias (__pthread_mutex_destroy, pthread_mutex_destroy)
363
364 int
365 attribute_hidden
366 __pthread_mutex_trylock(pthread_mutex_t * mutex)
367 {
368   pthread_descr self;
369   int retcode;
370
371   switch(mutex->__m_kind) {
372   case PTHREAD_MUTEX_ADAPTIVE_NP:
373     retcode = __pthread_trylock(&mutex->__m_lock);
374     return retcode;
375   case PTHREAD_MUTEX_RECURSIVE_NP:
376     self = thread_self();
377     if (mutex->__m_owner == self) {
378       mutex->__m_count++;
379       return 0;
380     }
381     retcode = __pthread_trylock(&mutex->__m_lock);
382     if (retcode == 0) {
383       mutex->__m_owner = self;
384       mutex->__m_count = 0;
385     }
386     return retcode;
387   case PTHREAD_MUTEX_ERRORCHECK_NP:
388     retcode = __pthread_alt_trylock(&mutex->__m_lock);
389     if (retcode == 0) {
390       mutex->__m_owner = thread_self();
391     }
392     return retcode;
393   case PTHREAD_MUTEX_TIMED_NP:
394     retcode = __pthread_alt_trylock(&mutex->__m_lock);
395     return retcode;
396   default:
397     return EINVAL;
398   }
399 }
400 strong_alias (__pthread_mutex_trylock, pthread_mutex_trylock)
401
402 int
403 attribute_hidden
404 __pthread_mutex_lock(pthread_mutex_t * mutex)
405 {
406   pthread_descr self;
407
408   switch(mutex->__m_kind) {
409   case PTHREAD_MUTEX_ADAPTIVE_NP:
410 #if 0
411     __pthread_lock(&mutex->__m_lock, NULL);
412 #else
413     pthread_mutex_lock_rep(mutex);
414 #endif
415     return 0;
416   case PTHREAD_MUTEX_RECURSIVE_NP:
417     self = thread_self();
418     if (mutex->__m_owner == self) {
419       mutex->__m_count++;
420       return 0;
421     }
422 #if 0
423     __pthread_lock(&mutex->__m_lock, self);
424 #else
425     pthread_mutex_lock_rep(mutex);
426 #endif
427     mutex->__m_owner = self;
428     mutex->__m_count = 0;
429     return 0;
430   case PTHREAD_MUTEX_ERRORCHECK_NP:
431     self = thread_self();
432     if (mutex->__m_owner == self) return EDEADLK;
433 #if 0
434     __pthread_alt_lock(&mutex->__m_lock, self);
435 #else
436     pthread_mutex_lock_rep(mutex);
437 #endif
438     mutex->__m_owner = self;
439     return 0;
440   case PTHREAD_MUTEX_TIMED_NP:
441 #if 0
442     __pthread_alt_lock(&mutex->__m_lock, NULL);
443 #else
444     pthread_mutex_lock_rep(mutex);
445 #endif
446     return 0;
447   default:
448     return EINVAL;
449   }
450 }
451
452 strong_alias (__pthread_mutex_lock, pthread_mutex_lock)
453
454
455 int
456 attribute_hidden
457 __pthread_mutex_timedlock (pthread_mutex_t *mutex,
458                                const struct timespec *abstime)
459 {
460   pthread_descr self;
461   int res;
462
463   if (__builtin_expect (abstime->tv_nsec, 0) < 0
464       || __builtin_expect (abstime->tv_nsec, 0) >= 1000000000)
465     return EINVAL;
466
467   switch(mutex->__m_kind) {
468   case PTHREAD_MUTEX_ADAPTIVE_NP:
469     __pthread_lock(&mutex->__m_lock, NULL);
470     return 0;
471   case PTHREAD_MUTEX_RECURSIVE_NP:
472     self = thread_self();
473     if (mutex->__m_owner == self) {
474       mutex->__m_count++;
475       return 0;
476     }
477     __pthread_lock(&mutex->__m_lock, self);
478     mutex->__m_owner = self;
479     mutex->__m_count = 0;
480     return 0;
481   case PTHREAD_MUTEX_ERRORCHECK_NP:
482     self = thread_self();
483     if (mutex->__m_owner == self) return EDEADLK;
484     res = __pthread_alt_timedlock(&mutex->__m_lock, self, abstime);
485     if (res != 0)
486       {
487         mutex->__m_owner = self;
488         return 0;
489       }
490     return ETIMEDOUT;
491   case PTHREAD_MUTEX_TIMED_NP:
492     /* Only this type supports timed out lock. */
493     return (__pthread_alt_timedlock(&mutex->__m_lock, NULL, abstime)
494             ? 0 : ETIMEDOUT);
495   default:
496     return EINVAL;
497   }
498 }
499 strong_alias (__pthread_mutex_timedlock, pthread_mutex_timedlock)
500
501 int
502 attribute_hidden
503 __pthread_mutex_unlock(pthread_mutex_t * mutex)
504 {
505   switch (mutex->__m_kind) {
506   case PTHREAD_MUTEX_ADAPTIVE_NP:
507 #if 0
508     __pthread_unlock(&mutex->__m_lock);
509 #else
510     pthread_mutex_unlock_rep(mutex);
511 #endif
512     return 0;
513   case PTHREAD_MUTEX_RECURSIVE_NP:
514     if (mutex->__m_owner != thread_self())
515       return EPERM;
516     if (mutex->__m_count > 0) {
517       mutex->__m_count--;
518       return 0;
519     }
520     mutex->__m_owner = NULL;
521 #if 0
522     __pthread_unlock(&mutex->__m_lock);
523 #else
524     pthread_mutex_unlock_rep(mutex);
525 #endif
526     return 0;
527   case PTHREAD_MUTEX_ERRORCHECK_NP:
528     if (mutex->__m_owner != thread_self() || mutex->__m_lock.__status == 0)
529       return EPERM;
530     mutex->__m_owner = NULL;
531 #if 0
532     __pthread_alt_unlock(&mutex->__m_lock);
533 #else
534     pthread_mutex_unlock_rep(mutex);
535 #endif
536     return 0;
537   case PTHREAD_MUTEX_TIMED_NP:
538 #if 0
539     __pthread_alt_unlock(&mutex->__m_lock);
540 #else
541     pthread_mutex_unlock_rep(mutex);
542 #endif
543     return 0;
544   default:
545     return EINVAL;
546   }
547 }
548 strong_alias (__pthread_mutex_unlock, pthread_mutex_unlock)
549
550 int
551 attribute_hidden
552 __pthread_mutexattr_init(pthread_mutexattr_t *attr)
553 {
554   attr->__mutexkind = PTHREAD_MUTEX_TIMED_NP;
555   return 0;
556 }
557 strong_alias (__pthread_mutexattr_init, pthread_mutexattr_init)
558
559 int
560 attribute_hidden
561 __pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
562 {
563   return 0;
564 }
565 strong_alias (__pthread_mutexattr_destroy, pthread_mutexattr_destroy)
566
567 int
568 attribute_hidden
569 __pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind)
570 {
571   if (kind != PTHREAD_MUTEX_ADAPTIVE_NP
572       && kind != PTHREAD_MUTEX_RECURSIVE_NP
573       && kind != PTHREAD_MUTEX_ERRORCHECK_NP
574       && kind != PTHREAD_MUTEX_TIMED_NP)
575     return EINVAL;
576   attr->__mutexkind = kind;
577   return 0;
578 }
579 weak_alias (__pthread_mutexattr_settype, pthread_mutexattr_settype)
580 strong_alias ( __pthread_mutexattr_settype, __pthread_mutexattr_setkind_np)
581 weak_alias (__pthread_mutexattr_setkind_np, pthread_mutexattr_setkind_np)
582
583 int
584 attribute_hidden
585 __pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *kind)
586 {
587   *kind = attr->__mutexkind;
588   return 0;
589 }
590 weak_alias (__pthread_mutexattr_gettype, pthread_mutexattr_gettype)
591 strong_alias (__pthread_mutexattr_gettype, __pthread_mutexattr_getkind_np)
592 weak_alias (__pthread_mutexattr_getkind_np, pthread_mutexattr_getkind_np)
593
594 int
595 attribute_hidden
596 __pthread_mutexattr_getpshared (const pthread_mutexattr_t *attr,
597                                    int *pshared)
598 {
599   *pshared = PTHREAD_PROCESS_PRIVATE;
600   return 0;
601 }
602 weak_alias (__pthread_mutexattr_getpshared, pthread_mutexattr_getpshared)
603
604 int
605 attribute_hidden
606 __pthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int pshared)
607 {
608   if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)
609     return EINVAL;
610
611   /* For now it is not possible to shared a conditional variable.  */
612   if (pshared != PTHREAD_PROCESS_PRIVATE)
613     return ENOSYS;
614
615   return 0;
616 }
617 weak_alias (__pthread_mutexattr_setpshared, pthread_mutexattr_setpshared)
618
619 /* Once-only execution */
620
621 static pthread_mutex_t once_masterlock = PTHREAD_MUTEX_INITIALIZER;
622 static pthread_cond_t once_finished = PTHREAD_COND_INITIALIZER;
623 static int fork_generation = 0; /* Child process increments this after fork. */
624
625 enum { NEVER = 0, IN_PROGRESS = 1, DONE = 2 };
626
627 /* If a thread is canceled while calling the init_routine out of
628    pthread once, this handler will reset the once_control variable
629    to the NEVER state. */
630
631 static void pthread_once_cancelhandler(void *arg)
632 {
633     pthread_once_t *once_control = arg;
634
635     pthread_mutex_lock(&once_masterlock);
636     *once_control = NEVER;
637     pthread_mutex_unlock(&once_masterlock);
638     pthread_cond_broadcast(&once_finished);
639 }
640
641 int
642 attribute_hidden
643 __pthread_once(pthread_once_t * once_control, void (*init_routine)(void))
644 {
645   /* flag for doing the condition broadcast outside of mutex */
646   int state_changed;
647
648   /* Test without locking first for speed */
649   if (*once_control == DONE) {
650     READ_MEMORY_BARRIER();
651     return 0;
652   }
653   /* Lock and test again */
654
655   state_changed = 0;
656
657   pthread_mutex_lock(&once_masterlock);
658
659   /* If this object was left in an IN_PROGRESS state in a parent
660      process (indicated by stale generation field), reset it to NEVER. */
661   if ((*once_control & 3) == IN_PROGRESS && (*once_control & ~3) != fork_generation)
662     *once_control = NEVER;
663
664   /* If init_routine is being called from another routine, wait until
665      it completes. */
666   while ((*once_control & 3) == IN_PROGRESS) {
667     pthread_cond_wait(&once_finished, &once_masterlock);
668   }
669   /* Here *once_control is stable and either NEVER or DONE. */
670   if (*once_control == NEVER) {
671     *once_control = IN_PROGRESS | fork_generation;
672     pthread_mutex_unlock(&once_masterlock);
673     pthread_cleanup_push(pthread_once_cancelhandler, once_control);
674     init_routine();
675     pthread_cleanup_pop(0);
676     pthread_mutex_lock(&once_masterlock);
677     WRITE_MEMORY_BARRIER();
678     *once_control = DONE;
679     state_changed = 1;
680   }
681   pthread_mutex_unlock(&once_masterlock);
682
683   if (state_changed)
684     pthread_cond_broadcast(&once_finished);
685
686   return 0;
687 }
688 strong_alias (__pthread_once, pthread_once)
689
690 /*
691  * Handle the state of the pthread_once mechanism across forks.  The
692  * once_masterlock is acquired in the parent process prior to a fork to ensure
693  * that no thread is in the critical region protected by the lock.  After the
694  * fork, the lock is released. In the child, the lock and the condition
695  * variable are simply reset.  The child also increments its generation
696  * counter which lets pthread_once calls detect stale IN_PROGRESS states
697  * and reset them back to NEVER.
698  */
699
700 void
701 attribute_hidden
702 __pthread_once_fork_prepare(void)
703 {
704   pthread_mutex_lock(&once_masterlock);
705 }
706
707 void
708 attribute_hidden
709 __pthread_once_fork_parent(void)
710 {
711   pthread_mutex_unlock(&once_masterlock);
712 }
713
714 void
715 attribute_hidden
716 __pthread_once_fork_child(void)
717 {
718   pthread_mutex_init(&once_masterlock, NULL);
719   pthread_cond_init(&once_finished, NULL);
720   if (fork_generation <= INT_MAX - 4)
721     fork_generation += 4;       /* leave least significant two bits zero */
722   else
723     fork_generation = 0;
724 }