/* doc: <routine name="rt_processor_new_private_queue" return_type="int" export="shared"> doc: <summary> Create a new private queue whose supplier is this processor. </summary> doc: <param name="self" type="struct rt_processor*"> The processor object. Must not be NULL. </param> doc: <param name="result" type="struct rt_private_queue**"> A pointer to the location where the result shall be stored. Must not be NULL. </param> doc: <return> T_OK on success. T_NO_MORE_MEMORY or a mutex creation error code when a resource could not be allocated. </return> doc: <thread_safety> Safe. </thread_safety> doc: <synchronization> None required. </synchronization> doc: </routine> */ int rt_processor_new_private_queue (struct rt_processor* self, struct rt_private_queue** result) { int error = T_OK; struct rt_private_queue* l_queue = NULL; REQUIRE ("self_not_nul", self); REQUIRE ("result_not_null", result); l_queue = (struct rt_private_queue*) malloc (sizeof (struct rt_private_queue)); if (l_queue) { error = rt_private_queue_init (l_queue, self); if (T_OK == error) { RT_TRACE (eif_pthread_mutex_lock (self->generated_private_queues_mutex)); error = private_queue_list_t_extend (&self->generated_private_queues, l_queue); RT_TRACE (eif_pthread_mutex_unlock (self->generated_private_queues_mutex)); } if (T_OK == error) { *result = l_queue; } else { /* An error occured. Free allocated resources and return. */ rt_private_queue_deinit (l_queue); free (l_queue); } } else { error = T_NO_MORE_MEMORY; } return error; }
/* doc: <routine name="rt_processor_publish_wait_condition" return_type="void" export="shared"> doc: <summary> Notify all processors in the 'self->wait_condition_subscribers' vector that a wait condition has changed. </summary> doc: <param name="self" type="struct rt_processor*"> The processor with the subscribers list. Must not be NULL. </param> doc: <thread_safety> Not safe. </thread_safety> doc: <synchronization> The feature rt_processor_subscribe_wait_condition must only be called when the thread executing 'self' is synchronized with a client. doc: This ensures that rt_publish_wait_condition cannot be executed at the same time. </synchronization> doc: </routine> */ rt_shared void rt_processor_publish_wait_condition (struct rt_processor* self) { struct subscriber_list_t* subscribers = NULL; REQUIRE ("self_not_null", self); subscribers = &self->wait_condition_subscribers; while (0 != subscriber_list_t_count(subscribers)) { struct rt_processor* item = subscriber_list_t_last (subscribers); subscriber_list_t_remove_last (subscribers); if (item) { /* Lock the registered processor's condition variable mutex. */ RT_TRACE (eif_pthread_mutex_lock (item->wait_condition_mutex)); /* Send a signal. */ RT_TRACE (eif_pthread_cond_signal (item->wait_condition)); /* Release the lock. */ RT_TRACE (eif_pthread_mutex_unlock (item->wait_condition_mutex)); } } }
/* doc: <routine name="rt_request_group_lock" return_type="void" export="shared"> doc: <summary> Lock all processors in the request group. </summary> doc: <param name="self" type="struct rt_request_group*"> The request group struct. Must not be NULL. </param> doc: <thread_safety> Not safe. </thread_safety> doc: <synchronization> None. </synchronization> doc: </routine> */ rt_shared void rt_request_group_lock (struct rt_request_group* self) { size_t i, l_count = rt_request_group_count (self); REQUIRE ("self_not_null", self); REQUIRE ("not_locked", !self->is_locked); /* We first need to sort the array based on the ID of the processor. * At a global scale, this avoids deadlocks and enables the * "atomic locking" guarantee of multiple arguments. */ if (!self->is_sorted) { /* The array is usually very small (1 to 5 items), so having * lightweight bubblesort algorithm is probably most efficient. */ bubble_sort (self->area, self->count); self->is_sorted = 1; } /* Temporarily lock the queue-of-queue of all suppliers. */ for (i = 0; i < l_count; ++i) { struct rt_processor* l_supplier = rt_request_group_item (self, i)->supplier; RT_TRACE (eif_pthread_mutex_lock (l_supplier->queue_of_queues_mutex)); } /* Add all private queues to the queue-of-queues */ for (i = 0; i < l_count; ++i) { rt_private_queue_lock (rt_request_group_item (self, i), self->client); } /* Release the queue-of-queue locks. */ for (i = 0; i < l_count; ++i) { struct rt_processor* l_supplier = rt_request_group_item (self, i)->supplier; RT_TRACE (eif_pthread_mutex_unlock (l_supplier->queue_of_queues_mutex)); } /* Synchronize with all passive processors. */ for (i=0; i < l_count; ++i) { struct rt_private_queue* l_queue = rt_request_group_item (self, i); if (l_queue->supplier->is_passive_region) { rt_private_queue_synchronize (l_queue, self->client); } } self->is_locked = 1; ENSURE ("sorted", self->is_sorted); ENSURE ("locked", self->is_locked); }
int print_err_msg (FILE *err, char *StrFmt, ...) { va_list ap; int r; FILE *exception_saved; char saved_cwd [PATH_MAX + 1]; eif_show_console (); /* Write error to `err'. */ va_start (ap, StrFmt); r = vfprintf (err, StrFmt, ap); va_end (ap); /* Now try to write error into `exception_trace.log' file */ #ifdef EIF_THREADS /* FIXME: This is not thread safe at all. */ if (!eif_exception_trace_mutex) { RT_TRACE(eif_pthread_mutex_create(&eif_exception_trace_mutex)); } if (eif_exception_trace_mutex) { RT_TRACE(eif_pthread_mutex_lock(eif_exception_trace_mutex)); } #endif getcwd(saved_cwd, PATH_MAX); chdir (starting_working_directory); /* If we are not allowed to write the exception, we don't do it */ if ((exception_saved = fopen( "exception_trace.log", "at" )) != NULL) { va_start (ap, StrFmt); r = vfprintf (exception_saved, StrFmt, ap); va_end (ap); fclose (exception_saved); } chdir (saved_cwd); #ifdef EIF_THREADS if (eif_exception_trace_mutex) { RT_TRACE(eif_pthread_mutex_unlock(eif_exception_trace_mutex)); } #endif return r; }
/* doc: <routine name="rt_request_group_wait" return_type="int" export="shared"> doc: <summary> doc: Release all locks and wait for a change notification from any processor in the group. doc: This feature is usually called after a wait condition fails. doc: It can only be called when the request group is locked. doc: Note: The wait() operation is blocking! </summary> doc: <param name="self" type="struct rt_request_group*"> The request group struct. Must not be NULL. </param> doc: <return> T_OK on success. T_NO_MORE_MEMORY if memory allocation fails, in which case the request group remains locked. </return> doc: <thread_safety> Not safe. </thread_safety> doc: <synchronization> None. </synchronization> doc: <fixme> Instead of unlocking normally after the wait condition, we could have a special 'unlock-after-wait-condition-failure'. doc: That way we can avoid sending unnecessary notifications after the evaluation of a wait condition. </fixme> doc: </routine> */ rt_shared int rt_request_group_wait (struct rt_request_group* self) { size_t i, l_count = rt_request_group_count (self); struct rt_processor* l_client = self->client; int error = T_OK; REQUIRE ("self_not_null", self); REQUIRE ("sorted", self->is_sorted); REQUIRE ("locked", self->is_locked); /* Register the current client with the suppliers, such that we * can later get a notification if a wait condition may have changed. */ for (i = 0; i < l_count; ++i) { struct rt_private_queue* l_queue = rt_request_group_item (self, i); /* We only register on queues which are currently synchronized. * Those are the ones that have executed a query during the wait * condition, and thus the only ones that matter. * Moreover, because the suppliers are currently synchronized, we * know that they cannot access their notification queue at the * moment, so we can safely modify the list from this thread. */ if (rt_private_queue_is_synchronized (l_queue)) { error = rt_private_queue_register_wait (l_queue, l_client); /* We bail out if we can't register for a wait condition change. */ if (error != T_OK) { return error; } } } /* Inform the GC that we're about to be blocked. */ EIF_ENTER_C; /* Before we unlock the synchronized queues, we have to acquire the * lock to our condition variable mutex. This has to happen before * rt_request_group_unlock to avoid missed signals. */ RT_TRACE (eif_pthread_mutex_lock (l_client->wait_condition_mutex)); /* Release the locks on the suppliers. After this statement they can * execute calls from other processors and signal back a wait condition * change. If we wouldn't hold the lock acquired in the previous step, * we might miss those signals and thus remain stuck in a wait condition * forever. */ rt_request_group_unlock (self); /* Now we perform the blocking wait on our condition. * This also releases the mutex, such that our suppliers may send signals to it. * Note: Usually these wait operations are performed inside a loop that checks whether * the wait condition became true. Our loop is compiler-generated however, * that's why we don't see it here. */ RT_TRACE (eif_pthread_cond_wait (l_client->wait_condition, l_client->wait_condition_mutex)); /* After the wakeup signal, we can release the mutex. * We're not interested in any further signals, as we re-register anyway if the * wait condition fails again. */ RT_TRACE (eif_pthread_mutex_unlock (l_client->wait_condition_mutex)); /* Synchronize with the GC again. */ EIF_EXIT_C; RTGC; /* Note: We do not clean up the registrations here, because it would involve * unnecessary locking and a risk of deadlocks. Instead, the suppliers delete * our registration during notification, and the GC will clean up any leftover registrations. */ ENSURE ("not_locked", !self->is_locked); return error; }