1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 
5 #include "config.h"
6 #include "runtime.h"
7 
8 #include <errno.h>
9 #include <stdlib.h>
10 #include <time.h>
11 #include <semaphore.h>
12 
13 /* If we don't have sem_timedwait, use pthread_cond_timedwait instead.
14    We don't always use condition variables because on some systems
15    pthread_mutex_lock and pthread_mutex_unlock must be called by the
16    same thread.  That is never true of semaphores.  */
17 
18 struct go_sem
19 {
20   sem_t sem;
21 
22 #ifndef HAVE_SEM_TIMEDWAIT
23   int timedwait;
24   pthread_mutex_t mutex;
25   pthread_cond_t cond;
26 #endif
27 };
28 
29 /* Create a semaphore.  */
30 
31 uintptr
runtime_semacreate(void)32 runtime_semacreate(void)
33 {
34   struct go_sem *p;
35 
36   /* Call malloc rather than runtime_malloc.  This will allocate space
37      on the C heap.  We can't call runtime_malloc here because it
38      could cause a deadlock.  */
39   p = malloc (sizeof (struct go_sem));
40   if (sem_init (&p->sem, 0, 0) != 0)
41     runtime_throw ("sem_init");
42 
43 #ifndef HAVE_SEM_TIMEDWAIT
44   if (pthread_mutex_init (&p->mutex, NULL) != 0)
45     runtime_throw ("pthread_mutex_init");
46   if (pthread_cond_init (&p->cond, NULL) != 0)
47     runtime_throw ("pthread_cond_init");
48 #endif
49 
50   return (uintptr) p;
51 }
52 
53 /* Acquire m->waitsema.  */
54 
55 int32
runtime_semasleep(int64 ns)56 runtime_semasleep (int64 ns)
57 {
58   M *m;
59   struct go_sem *sem;
60   int r;
61 
62   m = runtime_m ();
63   sem = (struct go_sem *) m->waitsema;
64   if (ns >= 0)
65     {
66       int64 abs;
67       struct timespec ts;
68       int err;
69 
70       abs = ns + runtime_nanotime ();
71       ts.tv_sec = abs / 1000000000LL;
72       ts.tv_nsec = abs % 1000000000LL;
73 
74       err = 0;
75 
76 #ifdef HAVE_SEM_TIMEDWAIT
77       r = sem_timedwait (&sem->sem, &ts);
78       if (r != 0)
79 	err = errno;
80 #else
81       if (pthread_mutex_lock (&sem->mutex) != 0)
82 	runtime_throw ("pthread_mutex_lock");
83 
84       while ((r = sem_trywait (&sem->sem)) != 0)
85 	{
86 	  r = pthread_cond_timedwait (&sem->cond, &sem->mutex, &ts);
87 	  if (r != 0)
88 	    {
89 	      err = r;
90 	      break;
91 	    }
92 	}
93 
94       if (pthread_mutex_unlock (&sem->mutex) != 0)
95 	runtime_throw ("pthread_mutex_unlock");
96 #endif
97 
98       if (err != 0)
99 	{
100 	  if (err == ETIMEDOUT || err == EAGAIN || err == EINTR)
101 	    return -1;
102 	  runtime_throw ("sema_timedwait");
103 	}
104       return 0;
105     }
106 
107   while (sem_wait (&sem->sem) != 0)
108     {
109       if (errno == EINTR)
110 	continue;
111       runtime_throw ("sem_wait");
112     }
113 
114   return 0;
115 }
116 
117 /* Wake up mp->waitsema.  */
118 
119 void
runtime_semawakeup(M * mp)120 runtime_semawakeup (M *mp)
121 {
122   struct go_sem *sem;
123 
124   sem = (struct go_sem *) mp->waitsema;
125   if (sem_post (&sem->sem) != 0)
126     runtime_throw ("sem_post");
127 
128 #ifndef HAVE_SEM_TIMEDWAIT
129   if (pthread_mutex_lock (&sem->mutex) != 0)
130     runtime_throw ("pthread_mutex_lock");
131   if (pthread_cond_broadcast (&sem->cond) != 0)
132     runtime_throw ("pthread_cond_broadcast");
133   if (pthread_mutex_unlock (&sem->mutex) != 0)
134     runtime_throw ("pthread_mutex_unlock");
135 #endif
136 }
137 
138 void
runtime_osinit(void)139 runtime_osinit (void)
140 {
141   runtime_ncpu = getproccount();
142 }
143 
144 void
runtime_goenvs(void)145 runtime_goenvs (void)
146 {
147   runtime_goenvs_unix ();
148 }
149