gboolean ec_lock_writer_trylock (ECLock *self) { gboolean result; g_static_rw_lock_reader_lock (self->free_lock); { g_assert (!self->destroyed); g_assert (!have_writer_lock_unprotected (self)); g_mutex_lock (self->state_mutex); if ( self->state != EC_LOCK_STATE_UNLOCKED ) { // Consistency check: we better not be able to successfully // trylock the independent lock either. gboolean lock_result = g_static_rw_lock_writer_trylock (self->lock); g_assert (lock_result == FALSE); maybe_trace (self, "failed to obtain writer lock in writer_trylock method"); result = FALSE; } else { // To make sure we haven't screwed up this class, we operate an // actual reader/writer lock. It had better work... gboolean lock_result = g_static_rw_lock_writer_trylock (self->lock); g_assert (lock_result == TRUE); // Also, the table of readers had better be empty. g_assert (g_hash_table_size (self->readers) == 0); // And there better not be any other writer either. g_assert (self->writer == NULL); self->writer = g_thread_self (); maybe_trace (self, "obtained writer lock in writer_trylock method"); self->state = EC_LOCK_STATE_WRITER_LOCKED; // No need to broadcast the new state here; other threads are // only interested when a writer lock is released! result = TRUE; } g_mutex_unlock (self->state_mutex); } g_static_rw_lock_reader_unlock (self->free_lock); return result; }
static gpointer test_g_static_rw_lock_thread (gpointer data) { while (test_g_static_rw_lock_run) { if (g_random_double() > .2) /* I'm a reader */ { if (g_random_double() > .2) /* I'll block */ g_static_rw_lock_reader_lock (&test_g_static_rw_lock_lock); else /* I'll only try */ if (!g_static_rw_lock_reader_trylock (&test_g_static_rw_lock_lock)) continue; G_LOCK (test_g_static_rw_lock_state); g_assert (test_g_static_rw_lock_state >= 0); test_g_static_rw_lock_state++; G_UNLOCK (test_g_static_rw_lock_state); g_usleep (g_random_int_range (20,1000)); G_LOCK (test_g_static_rw_lock_state); test_g_static_rw_lock_state--; G_UNLOCK (test_g_static_rw_lock_state); g_static_rw_lock_reader_unlock (&test_g_static_rw_lock_lock); } else /* I'm a writer */ { if (g_random_double() > .2) /* I'll block */ g_static_rw_lock_writer_lock (&test_g_static_rw_lock_lock); else /* I'll only try */ if (!g_static_rw_lock_writer_trylock (&test_g_static_rw_lock_lock)) continue; G_LOCK (test_g_static_rw_lock_state); g_assert (test_g_static_rw_lock_state == 0); test_g_static_rw_lock_state = -1; G_UNLOCK (test_g_static_rw_lock_state); g_usleep (g_random_int_range (20,1000)); G_LOCK (test_g_static_rw_lock_state); test_g_static_rw_lock_state = 0; G_UNLOCK (test_g_static_rw_lock_state); g_static_rw_lock_writer_unlock (&test_g_static_rw_lock_lock); } } return NULL; }
void ec_lock_free (ECLock *self) { // We are now going to destroy this instance, so nobody else better // be using it. gboolean lock_status = g_static_rw_lock_writer_trylock (self->free_lock); // As we said in the interface, its an error to free an instance in // use by any other method of this class (waiting in lock, being // queried for status, whatever -- all those uses are deemed naughty // race conditions). g_assert (lock_status == TRUE); { self->destroyed = TRUE; } g_static_rw_lock_writer_unlock (self->free_lock); g_mutex_lock (self->state_mutex); { g_assert (self->state == EC_LOCK_STATE_UNLOCKED); g_assert (g_hash_table_size (self->readers) == 0); g_assert (self->writer == NULL); } g_mutex_unlock (self->state_mutex); g_mutex_free (self->state_mutex); g_cond_free (self->state_cv); g_static_rw_lock_free (self->lock); g_free (self->lock); g_static_mutex_free (self->trace_lock); g_free (self->trace_lock); g_hash_table_destroy (self->readers); g_hash_table_destroy (self->traced_threads); g_hash_table_destroy (self->thread_names); g_static_rw_lock_free (self->free_lock); // FIIXME: I'm not certain now where I got the idea that we need // g_free as well as g_static_rw_lock_free, though I have a hazy // idea I may have looked at the source once upon a time to // determine this. g_free (self->free_lock); g_free (self); }
void ec_lock_writer_lock (ECLock *self) { g_static_rw_lock_reader_lock (self->free_lock); { g_assert (!self->destroyed); if ( have_writer_lock_unprotected (self) ) { GString *pts = pid_thread_string (); g_error ("%s: erroneous attempt to writer lock a lock already held", pts->str); } g_assert (!have_writer_lock_unprotected (self)); g_mutex_lock (self->state_mutex); while ( self->state != EC_LOCK_STATE_UNLOCKED ) { g_cond_wait (self->state_cv, self->state_mutex); } { // To make sure we haven't screwed up this class, we operate an // actual reader/writer lock. It had better work... gboolean lock_result = g_static_rw_lock_writer_trylock (self->lock); g_assert (lock_result == TRUE); // Also, the table of readers had better be empty. g_assert (g_hash_table_size (self->readers) == 0); // And there better not be any other writer either. g_assert (self->writer == NULL); self->writer = g_thread_self (); maybe_trace (self, "obtained writer lock"); self->state = EC_LOCK_STATE_WRITER_LOCKED; // No need to broadcast the new state here; other threads are // only interested when a writer lock is released! } g_mutex_unlock (self->state_mutex); } g_static_rw_lock_reader_unlock (self->free_lock); }