1 /* Copyright (c) 2007-2018. The SimGrid Team. All rights reserved. */
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. */
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"
13 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(simix_synchro, simix, "SIMIX Synchronization (mutex, semaphores and conditions)");
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);
20 /***************************** Raw synchronization *********************************/
22 smx_activity_t SIMIX_synchro_wait(sg_host_t smx_host, double timeout)
24 XBT_IN("(%p, %f)",smx_host,timeout);
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());
34 void SIMIX_synchro_stop_waiting(smx_actor_t process, smx_simcall_t simcall)
36 XBT_IN("(%p, %p)",process,simcall);
37 switch (simcall->call) {
39 case SIMCALL_MUTEX_LOCK:
40 simgrid::xbt::intrusive_erase(simcall_mutex_lock__get__mutex(simcall)->sleeping, *process);
43 case SIMCALL_COND_WAIT:
44 simgrid::xbt::intrusive_erase(simcall_cond_wait__get__cond(simcall)->sleeping, *process);
47 case SIMCALL_COND_WAIT_TIMEOUT:
48 simgrid::xbt::intrusive_erase(simcall_cond_wait_timeout__get__cond(simcall)->sleeping, *process);
51 case SIMCALL_SEM_ACQUIRE:
52 simgrid::xbt::intrusive_erase(simcall_sem_acquire__get__sem(simcall)->sleeping, *process);
55 case SIMCALL_SEM_ACQUIRE_TIMEOUT:
56 simgrid::xbt::intrusive_erase(simcall_sem_acquire_timeout__get__sem(simcall)->sleeping, *process);
65 void SIMIX_synchro_finish(smx_activity_t synchro)
67 XBT_IN("(%p)", synchro.get());
68 smx_simcall_t simcall = synchro->simcalls.front();
69 synchro->simcalls.pop_front();
71 switch (synchro->state) {
73 case SIMIX_SRC_TIMEOUT:
74 SMX_EXCEPTION(simcall->issuer, timeout_error, 0, "Synchro's wait timeout");
78 simcall->issuer->context->iwannadie = 1;
86 SIMIX_synchro_stop_waiting(simcall->issuer, simcall);
87 simcall->issuer->waiting_synchro = nullptr;
88 SIMIX_simcall_answer(simcall);
92 /********************************* Condition **********************************/
95 * \brief Initialize a condition.
97 * Allocates and creates the data for the condition.
98 * It have to be called before the use of the condition.
101 smx_cond_t SIMIX_cond_init()
104 smx_cond_t cond = new s_smx_cond_t();
111 * \brief Handle a condition waiting simcall without timeouts
112 * \param simcall the simcall
114 void simcall_HANDLER_cond_wait(smx_simcall_t simcall, smx_cond_t cond, smx_mutex_t mutex)
116 XBT_IN("(%p)",simcall);
117 smx_actor_t issuer = simcall->issuer;
119 _SIMIX_cond_wait(cond, mutex, -1, issuer, simcall);
124 * \brief Handle a condition waiting simcall with timeouts
125 * \param simcall the simcall
127 void simcall_HANDLER_cond_wait_timeout(smx_simcall_t simcall, smx_cond_t cond,
128 smx_mutex_t mutex, double timeout)
130 XBT_IN("(%p)",simcall);
131 smx_actor_t issuer = simcall->issuer;
133 _SIMIX_cond_wait(cond, mutex, timeout, issuer, simcall);
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)
141 XBT_IN("(%p, %p, %f, %p,%p)",cond,mutex,timeout,issuer,simcall);
142 smx_activity_t synchro = nullptr;
144 XBT_DEBUG("Wait condition %p", cond);
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) {
150 mutex->unlock(issuer);
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);
161 * \brief Signalizes a condition.
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
167 void SIMIX_cond_signal(smx_cond_t cond)
170 XBT_DEBUG("Signal condition %p", cond);
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();
178 /* Destroy waiter's synchronization */
179 proc.waiting_synchro = nullptr;
181 /* Now transform the cond wait simcall into a mutex lock one */
182 smx_simcall_t simcall = &proc.simcall;
184 if(simcall->call == SIMCALL_COND_WAIT)
185 mutex = simcall_cond_wait__get__mutex(simcall);
187 mutex = simcall_cond_wait_timeout__get__mutex(simcall);
188 simcall->call = SIMCALL_MUTEX_LOCK;
190 simcall_HANDLER_mutex_lock(simcall, mutex);
196 * \brief Broadcasts a condition.
198 * Signal ALL processes waiting on a condition.
199 * If there are no process waiting, no action is done.
200 * \param cond A condition
202 void SIMIX_cond_broadcast(smx_cond_t cond)
205 XBT_DEBUG("Broadcast condition %p", cond);
207 /* Signal the condition until nobody is waiting on it */
208 while (not cond->sleeping.empty()) {
209 SIMIX_cond_signal(cond);
214 smx_cond_t SIMIX_cond_ref(smx_cond_t cond)
217 intrusive_ptr_add_ref(cond);
221 void SIMIX_cond_unref(smx_cond_t cond)
224 XBT_DEBUG("Destroy condition %p", cond);
225 if (cond != nullptr) {
226 intrusive_ptr_release(cond);
232 void intrusive_ptr_add_ref(s_smx_cond_t *cond)
234 auto previous = cond->refcount_.fetch_add(1);
235 xbt_assert(previous != 0);
238 void intrusive_ptr_release(s_smx_cond_t *cond)
240 if (cond->refcount_.fetch_sub(1) == 1) {
241 xbt_assert(cond->sleeping.empty(), "Cannot destroy conditional since someone is still using it");
246 /******************************** Semaphores **********************************/
247 /** @brief Initialize a semaphore */
248 smx_sem_t SIMIX_sem_init(unsigned int value)
250 XBT_IN("(%u)",value);
251 smx_sem_t sem = new s_smx_sem_t;
257 /** @brief Destroys a semaphore */
258 void SIMIX_sem_destroy(smx_sem_t 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");
269 /** @brief release the semaphore
271 * Unlock a process waiting on the semaphore.
272 * If no one was blocked, the semaphore capacity is increased by 1.
274 void SIMIX_sem_release(smx_sem_t 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);
289 /** @brief Returns true if acquiring this semaphore would block */
290 int SIMIX_sem_would_block(smx_sem_t sem)
294 return (sem->value <= 0);
297 /** @brief Returns the current capacity of the semaphore */
298 int SIMIX_sem_get_capacity(smx_sem_t sem)
305 static void _SIMIX_sem_wait(smx_sem_t sem, double timeout, smx_actor_t issuer,
306 smx_simcall_t simcall)
308 XBT_IN("(%p, %f, %p, %p)",sem,timeout,issuer,simcall);
309 smx_activity_t synchro = nullptr;
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);
319 SIMIX_simcall_answer(simcall);
325 * \brief Handles a sem acquire simcall without timeout.
326 * \param simcall the simcall
328 void simcall_HANDLER_sem_acquire(smx_simcall_t simcall, smx_sem_t sem)
330 XBT_IN("(%p)",simcall);
331 _SIMIX_sem_wait(sem, -1, simcall->issuer, simcall);
336 * \brief Handles a sem acquire simcall with timeout.
337 * \param simcall the simcall
339 void simcall_HANDLER_sem_acquire_timeout(smx_simcall_t simcall, smx_sem_t sem, double timeout)
341 XBT_IN("(%p)",simcall);
342 _SIMIX_sem_wait(sem, timeout, simcall->issuer, simcall);