#include #include struct advanced_mutex { pthread_mutex_t lock; // access the structure pthread_cond_t cond_enter; // signalled when the worker thread is allowed to enter pthread_cond_t cond_safe; // signalled when the control thread becomes safe char may_enter; // flag: the worker thread may enter char wait_enter; // flag: the worker thread is waiting to enter char is_safe; // flag: the control thread is safe (working thread has not entered) char wait_safe; // flag: the control thread is waiting for safe }; typedef struct advanced_mutex adv_mutex; /* Initialize an advanced mutex. @param m - mutex pointer @param safe - flag: if !=0, place the mutex initially into a safe state, so that the wroker thread may not enter it until the control thread releases it; if 0, place it into the unsafe state and allow the worker thread to enter it freely */ void adv_mutex_init(adv_mutex *m, int safe) { pthread_mutex_init(&m->lock, NULL); m->may_enter = !safe; m->wait_enter = 0; pthread_cond_init(&m->cond_enter, NULL); m->is_safe = safe; m->wait_safe = 0; pthread_cond_init(&m->cond_safe, NULL); } /* Initialize an advanced mutex in the most typical way, placing it into an unsafe state. @param m - mutex pointer */ void adv_mutex_initsimple(adv_mutex *m) { adv_mutex_init(m, 0); } /* Destroy the contents of the advanced mutex. @param m - mutex pointer */ void adv_mutex_destroy(adv_mutex *m) { pthread_mutex_destroy(&m->lock); pthread_cond_destroy(&m->cond_enter); pthread_cond_destroy(&m->cond_safe); } /***************** Control thread interface *****************/ /* Request the mutex to enter the safe state. Never blocks. May be called repeatedly multiple times. @param m - mutex pointer */ void adv_mutex_request_safe(adv_mutex *m) { pthread_mutex_lock(&m->lock); m->may_enter = 0; pthread_mutex_unlock(&m->lock); } /* Block until the mutex enters the safe state (the worker thread leaves the protected region). Must be preceded by the request_safe(). @param m - mutex pointer */ void adv_mutex_wait_safe(adv_mutex *m) { pthread_mutex_lock(&m->lock); while (!m->is_safe) { m->wait_safe = 1; pthread_cond_wait(&m->cond_safe, &m->lock); m->wait_safe = 0; } pthread_mutex_unlock(&m->lock); } /* A convenience combination of request_safe() and wait_safe(). @param m - mutex pointer */ void adv_mutex_lock_safe(adv_mutex *m) { adv_mutex_request_safe(m); adv_mutex_wait_safe(m); } /* Release the mutex from the safe state. @param m - mutex pointer */ void adv_mutex_release_safe(adv_mutex *m) { pthread_mutex_lock(&m->lock); m->may_enter = 1; if (m->wait_enter) { pthread_cond_signal(&m->cond_enter); } pthread_mutex_unlock(&m->lock); } /***************** Worker thread interface *****************/ /* Attempt to enter the protected region. Never blocks. @param m - mutex pointer @return - !0 = on success, 0 on failure */ int adv_mutex_attempt_enter(adv_mutex *m) { pthread_mutex_lock(&m->lock); if (!m->may_enter) return 0; // keep the lock m->is_safe = 0; pthread_mutex_unlock(&m->lock); return 1; } /* After an enter attempt has failed and the caller has freed all the sensitive resources, block until the control thread releases the mutex and another attempt to enter may be made. @param m - mutex pointer */ void adv_mutex_continue_enter(adv_mutex *m) { while (!m->may_enter) { m->wait_enter = 1; pthread_cond_wait(&m->cond_enter, &m->lock); m->wait_enter = 0; } m->is_safe = 0; pthread_mutex_unlock(&m->lock); } /* A convenience wrapper for the situation when there are no sensitive resources to take care of, consists of an loop of attempt_enter()/continue_enter() until the entering succeeds. @param m - mutex pointer */ void adv_mutex_enter(adv_mutex *m) { while (!adv_mutex_attempt_enter(m)) adv_mutex_continue_enter(m); } /* In case if the worker thread does not wish to block, release any internal reservations done by attempt_enter(). @param m - mutex pointer */ void adv_mutex_giveup_enter(adv_mutex *m) { pthread_mutex_unlock(&m->lock); } /* Leave the protected region. @param m - mutex pointer */ void adv_mutex_leave(adv_mutex *m) { pthread_mutex_lock(&m->lock); m->is_safe = 1; if (m->wait_safe) { pthread_cond_signal(&m->cond_safe); } pthread_mutex_unlock(&m->lock); } /* a simple test */ adv_mutex am1, am2; void * thread1(void *arg /*ARGUSED*/) { fprintf(stderr, "entering mutex 1\n"); adv_mutex_enter(&am1); fprintf(stderr, "entered mutex 1\n"); sleep(2); fprintf(stderr, " thread1 sleep completed\n"); fprintf(stderr, "leaving mutex 1\n"); adv_mutex_leave(&am1); fprintf(stderr, "left mutex 1\n"); return NULL; } void * thread2(void *arg /*ARGUSED*/) { fprintf(stderr, "entering mutex 2\n"); while(!adv_mutex_attempt_enter(&am2)) { fprintf(stderr, "can not enter mutex 2\n"); adv_mutex_continue_enter(&am2); fprintf(stderr, "retry entering mutex 2\n"); } fprintf(stderr, "entered mutex 2\n"); sleep(1); fprintf(stderr, " thread2 sleep completed\n"); fprintf(stderr, "leaving mutex 2\n"); adv_mutex_leave(&am2); fprintf(stderr, "left mutex 2\n"); return NULL; } int main(int argc, char **argv) { pthread_t t1, t2; adv_mutex_init(&am1, 0); adv_mutex_init(&am2, 1); pthread_create(&t1, NULL, thread1, NULL); pthread_create(&t2, NULL, thread2, NULL); sleep(1); fprintf(stderr, " main sleep completed\n"); fprintf(stderr, "requesting safe mutex 1\n"); adv_mutex_request_safe(&am1); fprintf(stderr, "waiting for safe mutex 1\n"); adv_mutex_wait_safe(&am1); fprintf(stderr, "releasing safe mutex 1\n"); adv_mutex_release_safe(&am1); fprintf(stderr, "releasing safe mutex 2\n"); adv_mutex_release_safe(&am2); fprintf(stderr, "joining threads\n"); pthread_join(t1, NULL); pthread_join(t2, NULL); adv_mutex_destroy(&am1); adv_mutex_destroy(&am2); return 0; }