Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Use XBT_DEBUG instead of disabled fprintf(stderr).
[simgrid.git] / src / simix / smx_synchro.cpp
1 /* Copyright (c) 2007-2018. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 #include "smx_private.hpp"
7 #include "src/kernel/activity/MutexImpl.hpp"
8 #include "src/kernel/activity/SynchroRaw.hpp"
9 #include "src/simix/smx_synchro_private.hpp"
10 #include "src/surf/cpu_interface.hpp"
11 #include <xbt/ex.hpp>
12
13 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(simix_synchro, simix, "SIMIX Synchronization (mutex, semaphores and conditions)");
14
15 static void _SIMIX_cond_wait(smx_cond_t cond, smx_mutex_t mutex, double timeout,
16                              smx_actor_t issuer, smx_simcall_t simcall);
17 static void _SIMIX_sem_wait(smx_sem_t sem, double timeout, smx_actor_t issuer,
18                             smx_simcall_t simcall);
19
20 /***************************** Raw synchronization *********************************/
21
22 smx_activity_t SIMIX_synchro_wait(sg_host_t smx_host, double timeout)
23 {
24   XBT_IN("(%p, %f)",smx_host,timeout);
25
26   simgrid::kernel::activity::RawImplPtr sync =
27       simgrid::kernel::activity::RawImplPtr(new simgrid::kernel::activity::RawImpl());
28   sync->sleep                          = smx_host->pimpl_cpu->sleep(timeout);
29   sync->sleep->set_data(sync.get());
30   XBT_OUT();
31   return sync;
32 }
33
34 void SIMIX_synchro_stop_waiting(smx_actor_t process, smx_simcall_t simcall)
35 {
36   XBT_IN("(%p, %p)",process,simcall);
37   switch (simcall->call) {
38
39     case SIMCALL_MUTEX_LOCK:
40       simgrid::xbt::intrusive_erase(simcall_mutex_lock__get__mutex(simcall)->sleeping, *process);
41       break;
42
43     case SIMCALL_COND_WAIT:
44       simgrid::xbt::intrusive_erase(simcall_cond_wait__get__cond(simcall)->sleeping, *process);
45       break;
46
47     case SIMCALL_COND_WAIT_TIMEOUT:
48       simgrid::xbt::intrusive_erase(simcall_cond_wait_timeout__get__cond(simcall)->sleeping, *process);
49       break;
50
51     case SIMCALL_SEM_ACQUIRE:
52       simgrid::xbt::intrusive_erase(simcall_sem_acquire__get__sem(simcall)->sleeping, *process);
53       break;
54
55     case SIMCALL_SEM_ACQUIRE_TIMEOUT:
56       simgrid::xbt::intrusive_erase(simcall_sem_acquire_timeout__get__sem(simcall)->sleeping, *process);
57       break;
58
59     default:
60       THROW_IMPOSSIBLE;
61   }
62   XBT_OUT();
63 }
64
65 void SIMIX_synchro_finish(smx_activity_t synchro)
66 {
67   XBT_IN("(%p)", synchro.get());
68   smx_simcall_t simcall = synchro->simcalls.front();
69   synchro->simcalls.pop_front();
70
71   switch (synchro->state) {
72
73     case SIMIX_SRC_TIMEOUT:
74       SMX_EXCEPTION(simcall->issuer, timeout_error, 0, "Synchro's wait timeout");
75       break;
76
77     case SIMIX_FAILED:
78         simcall->issuer->context->iwannadie = 1;
79       break;
80
81     default:
82       THROW_IMPOSSIBLE;
83       break;
84   }
85
86   SIMIX_synchro_stop_waiting(simcall->issuer, simcall);
87   simcall->issuer->waiting_synchro = nullptr;
88   SIMIX_simcall_answer(simcall);
89   XBT_OUT();
90 }
91
92 /********************************* Condition **********************************/
93
94 /**
95  * \brief Initialize a condition.
96  *
97  * Allocates and creates the data for the condition.
98  * It have to be called before the use of the condition.
99  * \return A condition
100  */
101 smx_cond_t SIMIX_cond_init()
102 {
103   XBT_IN("()");
104   smx_cond_t cond = new s_smx_cond_t();
105   cond->refcount_ = 1;
106   XBT_OUT();
107   return cond;
108 }
109
110 /**
111  * \brief Handle a condition waiting simcall without timeouts
112  * \param simcall the simcall
113  */
114 void simcall_HANDLER_cond_wait(smx_simcall_t simcall, smx_cond_t cond, smx_mutex_t mutex)
115 {
116   XBT_IN("(%p)",simcall);
117   smx_actor_t issuer = simcall->issuer;
118
119   _SIMIX_cond_wait(cond, mutex, -1, issuer, simcall);
120   XBT_OUT();
121 }
122
123 /**
124  * \brief Handle a condition waiting simcall with timeouts
125  * \param simcall the simcall
126  */
127 void simcall_HANDLER_cond_wait_timeout(smx_simcall_t simcall, smx_cond_t cond,
128                      smx_mutex_t mutex, double timeout)
129 {
130   XBT_IN("(%p)",simcall);
131   smx_actor_t issuer = simcall->issuer;
132
133   _SIMIX_cond_wait(cond, mutex, timeout, issuer, simcall);
134   XBT_OUT();
135 }
136
137
138 static void _SIMIX_cond_wait(smx_cond_t cond, smx_mutex_t mutex, double timeout,
139                              smx_actor_t issuer, smx_simcall_t simcall)
140 {
141   XBT_IN("(%p, %p, %f, %p,%p)",cond,mutex,timeout,issuer,simcall);
142   smx_activity_t synchro = nullptr;
143
144   XBT_DEBUG("Wait condition %p", cond);
145
146   /* If there is a mutex unlock it */
147   /* FIXME: what happens if the issuer is not the owner of the mutex? */
148   if (mutex != nullptr) {
149     cond->mutex = mutex;
150     mutex->unlock(issuer);
151   }
152
153   synchro = SIMIX_synchro_wait(issuer->host, timeout);
154   synchro->simcalls.push_front(simcall);
155   issuer->waiting_synchro = synchro;
156   cond->sleeping.push_back(*simcall->issuer);
157   XBT_OUT();
158 }
159
160 /**
161  * \brief Signalizes a condition.
162  *
163  * Signalizes a condition and wakes up a sleeping process.
164  * If there are no process sleeping, no action is done.
165  * \param cond A condition
166  */
167 void SIMIX_cond_signal(smx_cond_t cond)
168 {
169   XBT_IN("(%p)",cond);
170   XBT_DEBUG("Signal condition %p", cond);
171
172   /* If there are processes waiting for the condition choose one and try
173      to make it acquire the mutex */
174   if (not cond->sleeping.empty()) {
175     auto& proc = cond->sleeping.front();
176     cond->sleeping.pop_front();
177
178     /* Destroy waiter's synchronization */
179     proc.waiting_synchro = nullptr;
180
181     /* Now transform the cond wait simcall into a mutex lock one */
182     smx_simcall_t simcall = &proc.simcall;
183     smx_mutex_t mutex;
184     if(simcall->call == SIMCALL_COND_WAIT)
185       mutex = simcall_cond_wait__get__mutex(simcall);
186     else
187       mutex = simcall_cond_wait_timeout__get__mutex(simcall);
188     simcall->call = SIMCALL_MUTEX_LOCK;
189
190     simcall_HANDLER_mutex_lock(simcall, mutex);
191   }
192   XBT_OUT();
193 }
194
195 /**
196  * \brief Broadcasts a condition.
197  *
198  * Signal ALL processes waiting on a condition.
199  * If there are no process waiting, no action is done.
200  * \param cond A condition
201  */
202 void SIMIX_cond_broadcast(smx_cond_t cond)
203 {
204   XBT_IN("(%p)",cond);
205   XBT_DEBUG("Broadcast condition %p", cond);
206
207   /* Signal the condition until nobody is waiting on it */
208   while (not cond->sleeping.empty()) {
209     SIMIX_cond_signal(cond);
210   }
211   XBT_OUT();
212 }
213
214 smx_cond_t SIMIX_cond_ref(smx_cond_t cond)
215 {
216   if (cond != nullptr)
217     intrusive_ptr_add_ref(cond);
218   return cond;
219 }
220
221 void SIMIX_cond_unref(smx_cond_t cond)
222 {
223   XBT_IN("(%p)",cond);
224   XBT_DEBUG("Destroy condition %p", cond);
225   if (cond != nullptr) {
226     intrusive_ptr_release(cond);
227   }
228   XBT_OUT();
229 }
230
231
232 void intrusive_ptr_add_ref(s_smx_cond_t *cond)
233 {
234   auto previous = cond->refcount_.fetch_add(1);
235   xbt_assert(previous != 0);
236 }
237
238 void intrusive_ptr_release(s_smx_cond_t *cond)
239 {
240   if (cond->refcount_.fetch_sub(1) == 1) {
241     xbt_assert(cond->sleeping.empty(), "Cannot destroy conditional since someone is still using it");
242     delete cond;
243   }
244 }
245
246 /******************************** Semaphores **********************************/
247 /** @brief Initialize a semaphore */
248 smx_sem_t SIMIX_sem_init(unsigned int value)
249 {
250   XBT_IN("(%u)",value);
251   smx_sem_t sem = new s_smx_sem_t;
252   sem->value = value;
253   XBT_OUT();
254   return sem;
255 }
256
257 /** @brief Destroys a semaphore */
258 void SIMIX_sem_destroy(smx_sem_t sem)
259 {
260   XBT_IN("(%p)",sem);
261   XBT_DEBUG("Destroy semaphore %p", sem);
262   if (sem != nullptr) {
263     xbt_assert(sem->sleeping.empty(), "Cannot destroy semaphore since someone is still using it");
264     delete sem;
265   }
266   XBT_OUT();
267 }
268
269 /** @brief release the semaphore
270  *
271  * Unlock a process waiting on the semaphore.
272  * If no one was blocked, the semaphore capacity is increased by 1.
273  */
274 void SIMIX_sem_release(smx_sem_t sem)
275 {
276   XBT_IN("(%p)",sem);
277   XBT_DEBUG("Sem release semaphore %p", sem);
278   if (not sem->sleeping.empty()) {
279     auto& proc = sem->sleeping.front();
280     sem->sleeping.pop_front();
281     proc.waiting_synchro = nullptr;
282     SIMIX_simcall_answer(&proc.simcall);
283   } else {
284     sem->value++;
285   }
286   XBT_OUT();
287 }
288
289 /** @brief Returns true if acquiring this semaphore would block */
290 int SIMIX_sem_would_block(smx_sem_t sem)
291 {
292   XBT_IN("(%p)",sem);
293   XBT_OUT();
294   return (sem->value <= 0);
295 }
296
297 /** @brief Returns the current capacity of the semaphore */
298 int SIMIX_sem_get_capacity(smx_sem_t sem)
299 {
300   XBT_IN("(%p)",sem);
301   XBT_OUT();
302   return sem->value;
303 }
304
305 static void _SIMIX_sem_wait(smx_sem_t sem, double timeout, smx_actor_t issuer,
306                             smx_simcall_t simcall)
307 {
308   XBT_IN("(%p, %f, %p, %p)",sem,timeout,issuer,simcall);
309   smx_activity_t synchro = nullptr;
310
311   XBT_DEBUG("Wait semaphore %p (timeout:%f)", sem, timeout);
312   if (sem->value <= 0) {
313     synchro = SIMIX_synchro_wait(issuer->host, timeout);
314     synchro->simcalls.push_front(simcall);
315     issuer->waiting_synchro = synchro;
316     sem->sleeping.push_back(*issuer);
317   } else {
318     sem->value--;
319     SIMIX_simcall_answer(simcall);
320   }
321   XBT_OUT();
322 }
323
324 /**
325  * \brief Handles a sem acquire simcall without timeout.
326  * \param simcall the simcall
327  */
328 void simcall_HANDLER_sem_acquire(smx_simcall_t simcall, smx_sem_t sem)
329 {
330   XBT_IN("(%p)",simcall);
331   _SIMIX_sem_wait(sem, -1, simcall->issuer, simcall);
332   XBT_OUT();
333 }
334
335 /**
336  * \brief Handles a sem acquire simcall with timeout.
337  * \param simcall the simcall
338  */
339 void simcall_HANDLER_sem_acquire_timeout(smx_simcall_t simcall, smx_sem_t sem, double timeout)
340 {
341   XBT_IN("(%p)",simcall);
342   _SIMIX_sem_wait(sem, timeout, simcall->issuer, simcall);
343   XBT_OUT();
344 }