Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of scm.gforge.inria.fr:/gitroot/simgrid/simgrid
[simgrid.git] / src / simix / smx_synchro.cpp
1 /* Copyright (c) 2007-2015. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #include "src/surf/surf_interface.hpp"
8 #include "smx_private.h"
9 #include "xbt/log.h"
10
11 #include "src/simix/SynchroRaw.hpp"
12
13 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(simix_synchro, simix,
14                                 "SIMIX Synchronization (mutex, semaphores and conditions)");
15
16 static smx_synchro_t SIMIX_synchro_wait(sg_host_t smx_host, double timeout);
17 static void SIMIX_synchro_finish(smx_synchro_t synchro);
18 static void _SIMIX_cond_wait(smx_cond_t cond, smx_mutex_t mutex, double timeout,
19                              smx_process_t issuer, smx_simcall_t simcall);
20 static void _SIMIX_sem_wait(smx_sem_t sem, double timeout, smx_process_t issuer,
21                             smx_simcall_t simcall);
22
23 /***************************** Raw synchronization *********************************/
24
25 static smx_synchro_t SIMIX_synchro_wait(sg_host_t smx_host, double timeout)
26 {
27   XBT_IN("(%p, %f)",smx_host,timeout);
28
29   simgrid::simix::Raw *sync = new simgrid::simix::Raw();
30   sync->name = nullptr;
31   sync->sleep = surf_host_sleep(smx_host, timeout);
32   sync->sleep->setData(sync);
33   XBT_OUT();
34   return sync;
35 }
36
37 void SIMIX_synchro_stop_waiting(smx_process_t process, smx_simcall_t simcall)
38 {
39   XBT_IN("(%p, %p)",process,simcall);
40   switch (simcall->call) {
41
42     case SIMCALL_MUTEX_LOCK:
43       xbt_swag_remove(process, simcall_mutex_lock__get__mutex(simcall)->sleeping);
44       break;
45
46     case SIMCALL_COND_WAIT:
47       xbt_swag_remove(process, simcall_cond_wait__get__cond(simcall)->sleeping);
48       break;
49
50     case SIMCALL_COND_WAIT_TIMEOUT:
51       xbt_swag_remove(process, simcall_cond_wait_timeout__get__cond(simcall)->sleeping);
52       break;
53
54     case SIMCALL_SEM_ACQUIRE:
55       xbt_swag_remove(process, simcall_sem_acquire__get__sem(simcall)->sleeping);
56       break;
57
58     case SIMCALL_SEM_ACQUIRE_TIMEOUT:
59       xbt_swag_remove(process, simcall_sem_acquire_timeout__get__sem(simcall)->sleeping);
60       break;
61
62     default:
63       THROW_IMPOSSIBLE;
64   }
65   XBT_OUT();
66 }
67
68 void SIMIX_synchro_destroy(smx_synchro_t synchro)
69 {
70   XBT_DEBUG("Destroying synchro %p", synchro);
71   simgrid::simix::Raw *raw = static_cast<simgrid::simix::Raw*>(synchro);
72
73   raw->sleep->unref();
74   delete raw;
75 }
76
77 void SIMIX_post_synchro(smx_synchro_t synchro)
78 {
79   XBT_IN("(%p)",synchro);
80   simgrid::simix::Raw *raw = static_cast<simgrid::simix::Raw*>(synchro);
81   if (raw->sleep->getState() == simgrid::surf::Action::State::failed)
82     raw->state = SIMIX_FAILED;
83   else if(raw->sleep->getState() == simgrid::surf::Action::State::done)
84     raw->state = SIMIX_SRC_TIMEOUT;
85
86   SIMIX_synchro_finish(raw);
87   XBT_OUT();
88 }
89
90 static void SIMIX_synchro_finish(smx_synchro_t synchro)
91 {
92   XBT_IN("(%p)",synchro);
93   smx_simcall_t simcall = (smx_simcall_t) xbt_fifo_shift(synchro->simcalls);
94
95   switch (synchro->state) {
96
97     case SIMIX_SRC_TIMEOUT:
98       SMX_EXCEPTION(simcall->issuer, timeout_error, 0, "Synchro's wait timeout");
99       break;
100
101     case SIMIX_FAILED:
102         simcall->issuer->context->iwannadie = 1;
103 //      SMX_EXCEPTION(simcall->issuer, host_error, 0, "Host failed");
104       break;
105
106     default:
107       THROW_IMPOSSIBLE;
108       break;
109   }
110
111   SIMIX_synchro_stop_waiting(simcall->issuer, simcall);
112   simcall->issuer->waiting_synchro = NULL;
113   SIMIX_synchro_destroy(synchro);
114   SIMIX_simcall_answer(simcall);
115   XBT_OUT();
116 }
117 /*********************************** Mutex ************************************/
118
119 smx_mutex_t simcall_HANDLER_mutex_init(smx_simcall_t simcall){
120   return SIMIX_mutex_init();
121 }
122 /**
123  * \brief Initialize a mutex.
124  *
125  * Allocs and creates the data for the mutex.
126  * \return A mutex
127  */
128 smx_mutex_t SIMIX_mutex_init(void)
129 {
130   XBT_IN("()");
131   s_smx_process_t p;            /* useful to initialize sleeping swag */
132
133   smx_mutex_t mutex = xbt_new0(s_smx_mutex_t, 1);
134   mutex->locked = 0;
135   mutex->sleeping = xbt_swag_new(xbt_swag_offset(p, synchro_hookup));
136   XBT_OUT();
137   return mutex;
138 }
139
140 /**
141  * \brief Handles a mutex lock simcall.
142  * \param simcall the simcall
143  */
144 void simcall_HANDLER_mutex_lock(smx_simcall_t simcall, smx_mutex_t mutex)
145 {
146   XBT_IN("(%p)",simcall);
147   /* FIXME: check where to validate the arguments */
148   smx_synchro_t synchro = NULL;
149   smx_process_t process = simcall->issuer;
150
151   if (mutex->locked) {
152     /* FIXME: check if the host is active ? */
153     /* Somebody using the mutex, use a synchronization to get host failures */
154     synchro = SIMIX_synchro_wait(process->host, -1);
155     xbt_fifo_push(synchro->simcalls, simcall);
156     simcall->issuer->waiting_synchro = synchro;
157     xbt_swag_insert(simcall->issuer, mutex->sleeping);   
158   } else {
159     /* mutex free */
160     mutex->locked = 1;
161     mutex->owner = simcall->issuer;
162     SIMIX_simcall_answer(simcall);
163   }
164   XBT_OUT();
165 }
166
167 int simcall_HANDLER_mutex_trylock(smx_simcall_t simcall, smx_mutex_t mutex){
168   return SIMIX_mutex_trylock(mutex, simcall->issuer);
169 }
170 /**
171  * \brief Tries to lock a mutex.
172  *
173  * Tries to lock a mutex, return 1 if the mutex is unlocked, else 0.
174  * This function does not block and wait for the mutex to be unlocked.
175  * \param mutex The mutex
176  * \param issuer The process that tries to acquire the mutex
177  * \return 1 - mutex free, 0 - mutex used
178  */
179 int SIMIX_mutex_trylock(smx_mutex_t mutex, smx_process_t issuer)
180 {
181   XBT_IN("(%p, %p)",mutex,issuer);
182   if (mutex->locked){
183     XBT_OUT();
184     return 0;
185   }
186
187   mutex->locked = 1;
188   mutex->owner = issuer;
189   XBT_OUT();
190   return 1;
191 }
192
193 void simcall_HANDLER_mutex_unlock(smx_simcall_t simcall, smx_mutex_t mutex){
194    SIMIX_mutex_unlock(mutex, simcall->issuer);
195 }
196 /**
197  * \brief Unlocks a mutex.
198  *
199  * Unlocks the mutex and gives it to a process waiting for it. 
200  * If the unlocker is not the owner of the mutex nothing happens.
201  * If there are no process waiting, it sets the mutex as free.
202  * \param mutex The mutex
203  * \param issuer The process trying to unlock the mutex
204  */
205 void SIMIX_mutex_unlock(smx_mutex_t mutex, smx_process_t issuer)
206 {
207   XBT_IN("(%p, %p)",mutex,issuer);
208
209   /* If the mutex is not owned by the issuer, that's not good */
210   if (issuer != mutex->owner)
211     THROWF(mismatch_error, 0, "Cannot release that mutex: it was locked by %s (pid:%d), not by you.",
212         SIMIX_process_get_name(mutex->owner),SIMIX_process_get_PID(mutex->owner));
213
214   if (xbt_swag_size(mutex->sleeping) > 0) {
215     /*process to wake up */
216     smx_process_t p = (smx_process_t) xbt_swag_extract(mutex->sleeping);
217     SIMIX_synchro_destroy(p->waiting_synchro);
218     p->waiting_synchro = NULL;
219     mutex->owner = p;
220     SIMIX_simcall_answer(&p->simcall);
221   } else {
222     /* nobody to wake up */
223     mutex->locked = 0;
224     mutex->owner = NULL;
225   }
226   XBT_OUT();
227 }
228
229 /**
230  * \brief Destroys a mutex.
231  *
232  * Destroys and frees the mutex's memory. 
233  * \param mutex A mutex
234  */
235 void SIMIX_mutex_destroy(smx_mutex_t mutex)
236 {
237   XBT_IN("(%p)",mutex);
238   if (mutex){
239     xbt_swag_free(mutex->sleeping);
240     xbt_free(mutex);
241   }
242   XBT_OUT();
243 }
244
245 /********************************* Condition **********************************/
246
247 /**
248  * \brief Initialize a condition.
249  *
250  * Allocates and creates the data for the condition.
251  * It have to be called before the use of the condition.
252  * \return A condition
253  */
254 smx_cond_t SIMIX_cond_init(void)
255 {
256   XBT_IN("()");
257   s_smx_process_t p;
258   smx_cond_t cond = xbt_new0(s_smx_cond_t, 1);
259   cond->sleeping = xbt_swag_new(xbt_swag_offset(p, synchro_hookup));
260   cond->mutex = NULL;
261   XBT_OUT();
262   return cond;
263 }
264
265 /**
266  * \brief Handle a condition waiting simcall without timeouts
267  * \param simcall the simcall
268  */
269 void simcall_HANDLER_cond_wait(smx_simcall_t simcall, smx_cond_t cond, smx_mutex_t mutex)
270 {
271   XBT_IN("(%p)",simcall);
272   smx_process_t issuer = simcall->issuer;
273
274   _SIMIX_cond_wait(cond, mutex, -1, issuer, simcall);
275   XBT_OUT();
276 }
277
278 /**
279  * \brief Handle a condition waiting simcall with timeouts
280  * \param simcall the simcall
281  */
282 void simcall_HANDLER_cond_wait_timeout(smx_simcall_t simcall, smx_cond_t cond,
283                      smx_mutex_t mutex, double timeout)
284 {
285   XBT_IN("(%p)",simcall);
286   smx_process_t issuer = simcall->issuer;
287
288   _SIMIX_cond_wait(cond, mutex, timeout, issuer, simcall);
289   XBT_OUT();
290 }
291
292
293 static void _SIMIX_cond_wait(smx_cond_t cond, smx_mutex_t mutex, double timeout,
294                              smx_process_t issuer, smx_simcall_t simcall)
295 {
296   XBT_IN("(%p, %p, %f, %p,%p)",cond,mutex,timeout,issuer,simcall);
297   smx_synchro_t synchro = NULL;
298
299   XBT_DEBUG("Wait condition %p", cond);
300
301   /* If there is a mutex unlock it */
302   /* FIXME: what happens if the issuer is not the owner of the mutex? */
303   if (mutex != NULL) {
304     cond->mutex = mutex;
305     SIMIX_mutex_unlock(mutex, issuer);
306   }
307
308   synchro = SIMIX_synchro_wait(issuer->host, timeout);
309   xbt_fifo_unshift(synchro->simcalls, simcall);
310   issuer->waiting_synchro = synchro;
311   xbt_swag_insert(simcall->issuer, cond->sleeping);   
312   XBT_OUT();
313 }
314
315 /**
316  * \brief Signalizes a condition.
317  *
318  * Signalizes a condition and wakes up a sleeping process. 
319  * If there are no process sleeping, no action is done.
320  * \param cond A condition
321  */
322 void SIMIX_cond_signal(smx_cond_t cond)
323 {
324   XBT_IN("(%p)",cond);
325   smx_process_t proc = NULL;
326   smx_mutex_t mutex = NULL;
327   smx_simcall_t simcall = NULL;
328
329   XBT_DEBUG("Signal condition %p", cond);
330
331   /* If there are processes waiting for the condition choose one and try 
332      to make it acquire the mutex */
333   if ((proc = (smx_process_t) xbt_swag_extract(cond->sleeping))) {
334
335     /* Destroy waiter's synchronization */
336     SIMIX_synchro_destroy(proc->waiting_synchro);
337     proc->waiting_synchro = NULL;
338
339     /* Now transform the cond wait simcall into a mutex lock one */
340     simcall = &proc->simcall;
341     if(simcall->call == SIMCALL_COND_WAIT)
342       mutex = simcall_cond_wait__get__mutex(simcall);
343     else
344       mutex = simcall_cond_wait_timeout__get__mutex(simcall);
345     simcall->call = SIMCALL_MUTEX_LOCK;
346
347     simcall_HANDLER_mutex_lock(simcall, mutex);
348   }
349   XBT_OUT();
350 }
351
352 /**
353  * \brief Broadcasts a condition.
354  *
355  * Signal ALL processes waiting on a condition.
356  * If there are no process waiting, no action is done.
357  * \param cond A condition
358  */
359 void SIMIX_cond_broadcast(smx_cond_t cond)
360 {
361   XBT_IN("(%p)",cond);
362   XBT_DEBUG("Broadcast condition %p", cond);
363
364   /* Signal the condition until nobody is waiting on it */
365   while (xbt_swag_size(cond->sleeping)) {
366     SIMIX_cond_signal(cond);
367   }
368   XBT_OUT();
369 }
370
371 /**
372  * \brief Destroys a condition.
373  *
374  * Destroys and frees the condition's memory. 
375  * \param cond A condition
376  */
377 void SIMIX_cond_destroy(smx_cond_t cond)
378 {
379   XBT_IN("(%p)",cond);
380   XBT_DEBUG("Destroy condition %p", cond);
381
382   if (cond != NULL) {
383     xbt_assert(xbt_swag_size(cond->sleeping) == 0,
384                 "Cannot destroy conditional since someone is still using it");
385
386     xbt_swag_free(cond->sleeping);
387     xbt_free(cond);
388   }
389   XBT_OUT();
390 }
391
392 /******************************** Semaphores **********************************/
393 #define SMX_SEM_NOLIMIT 99999
394 /** @brief Initialize a semaphore */
395 smx_sem_t SIMIX_sem_init(unsigned int value)
396 {
397   XBT_IN("(%u)",value);
398   s_smx_process_t p;
399
400   smx_sem_t sem = xbt_new0(s_smx_sem_t, 1);
401   sem->sleeping = xbt_swag_new(xbt_swag_offset(p, synchro_hookup));
402   sem->value = value;
403   XBT_OUT();
404   return sem;
405 }
406
407 /** @brief Destroys a semaphore */
408 void SIMIX_sem_destroy(smx_sem_t sem)
409 {
410   XBT_IN("(%p)",sem);
411   XBT_DEBUG("Destroy semaphore %p", sem);
412   if (sem != NULL) {
413     xbt_assert(xbt_swag_size(sem->sleeping) == 0,
414                 "Cannot destroy semaphore since someone is still using it");
415     xbt_swag_free(sem->sleeping);
416     xbt_free(sem);
417   }
418   XBT_OUT();
419 }
420
421 void simcall_HANDLER_sem_release(smx_simcall_t simcall, smx_sem_t sem){
422   SIMIX_sem_release(sem);
423 }
424 /** @brief release the semaphore
425  *
426  * Unlock a process waiting on the semaphore.
427  * If no one was blocked, the semaphore capacity is increased by 1.
428  */
429 void SIMIX_sem_release(smx_sem_t sem)
430 {
431   XBT_IN("(%p)",sem);
432   smx_process_t proc;
433
434   XBT_DEBUG("Sem release semaphore %p", sem);
435   if ((proc = (smx_process_t) xbt_swag_extract(sem->sleeping))) {
436     SIMIX_synchro_destroy(proc->waiting_synchro);
437     proc->waiting_synchro = NULL;
438     SIMIX_simcall_answer(&proc->simcall);
439   } else if (sem->value < SMX_SEM_NOLIMIT) {
440     sem->value++;
441   }
442   XBT_OUT();
443 }
444
445 /** @brief Returns true if acquiring this semaphore would block */
446 int SIMIX_sem_would_block(smx_sem_t sem)
447 {
448   XBT_IN("(%p)",sem);
449   XBT_OUT();
450   return (sem->value <= 0);
451 }
452
453 int simcall_HANDLER_sem_get_capacity(smx_simcall_t simcall, smx_sem_t sem){
454   return SIMIX_sem_get_capacity(sem);
455 }
456 /** @brief Returns the current capacity of the semaphore */
457 int SIMIX_sem_get_capacity(smx_sem_t sem)
458 {
459   XBT_IN("(%p)",sem);
460   XBT_OUT();
461   return sem->value;
462 }
463
464 static void _SIMIX_sem_wait(smx_sem_t sem, double timeout, smx_process_t issuer,
465                             smx_simcall_t simcall)
466 {
467   XBT_IN("(%p, %f, %p, %p)",sem,timeout,issuer,simcall);
468   smx_synchro_t synchro = NULL;
469
470   XBT_DEBUG("Wait semaphore %p (timeout:%f)", sem, timeout);
471   if (sem->value <= 0) {
472     synchro = SIMIX_synchro_wait(issuer->host, timeout);
473     xbt_fifo_unshift(synchro->simcalls, simcall);
474     issuer->waiting_synchro = synchro;
475     xbt_swag_insert(issuer, sem->sleeping);
476   } else {
477     sem->value--;
478     SIMIX_simcall_answer(simcall);
479   }
480   XBT_OUT();
481 }
482
483 /**
484  * \brief Handles a sem acquire simcall without timeout.
485  * \param simcall the simcall
486  */
487 void simcall_HANDLER_sem_acquire(smx_simcall_t simcall, smx_sem_t sem)
488 {
489   XBT_IN("(%p)",simcall);
490   _SIMIX_sem_wait(sem, -1, simcall->issuer, simcall);
491   XBT_OUT();
492 }
493
494 /**
495  * \brief Handles a sem acquire simcall with timeout.
496  * \param simcall the simcall
497  */
498 void simcall_HANDLER_sem_acquire_timeout(smx_simcall_t simcall, smx_sem_t sem, double timeout)
499 {
500   XBT_IN("(%p)",simcall);
501   _SIMIX_sem_wait(sem, timeout, simcall->issuer, simcall);  
502   XBT_OUT();
503 }
504 int simcall_HANDLER_sem_would_block(smx_simcall_t simcall, smx_sem_t sem) {
505   return SIMIX_sem_would_block(sem);
506 }