session CHANGE concurrency greatly improved
Enables concurrent polling of a single pollsession
structure and new session creation.
diff --git a/src/session.c b/src/session.c
index d61d10e..818d71d 100644
--- a/src/session.c
+++ b/src/session.c
@@ -128,13 +128,39 @@
}
#endif
+struct nc_session *
+nc_new_session(int not_allocate_ti)
+{
+ struct nc_session *sess;
+
+ sess = calloc(1, sizeof *sess);
+ if (!sess) {
+ return NULL;
+ }
+
+ if (!not_allocate_ti) {
+ sess->ti_lock = malloc(sizeof *sess->ti_lock);
+ sess->ti_cond = malloc(sizeof *sess->ti_cond);
+ sess->ti_inuse = malloc(sizeof *sess->ti_inuse);
+ if (!sess->ti_lock || !sess->ti_cond || !sess->ti_inuse) {
+ free(sess->ti_lock);
+ free(sess->ti_cond);
+ free((int *)sess->ti_inuse);
+ free(sess);
+ return NULL;
+ }
+ }
+
+ return sess;
+}
+
/*
* @return 1 - success
* 0 - timeout
* -1 - error
*/
int
-nc_timedlock(pthread_mutex_t *lock, int timeout, const char *func)
+nc_session_lock(struct nc_session *session, int timeout, const char *func)
{
int ret;
struct timespec ts_timeout;
@@ -145,27 +171,117 @@
ts_timeout.tv_sec += timeout / 1000;
ts_timeout.tv_nsec += (timeout % 1000) * 1000000;
- ret = pthread_mutex_timedlock(lock, &ts_timeout);
+ /* LOCK */
+ ret = pthread_mutex_timedlock(session->ti_lock, &ts_timeout);
+ if (!ret) {
+ while (*session->ti_inuse) {
+ ret = pthread_cond_timedwait(session->ti_cond, session->ti_lock, &ts_timeout);
+ if (ret) {
+ pthread_mutex_unlock(session->ti_lock);
+ break;
+ }
+ }
+ }
} else if (!timeout) {
- ret = pthread_mutex_trylock(lock);
- if (ret == EBUSY) {
- /* equivalent in this case */
- ret = ETIMEDOUT;
+ if (*session->ti_inuse) {
+ /* immediate timeout */
+ return 0;
+ }
+
+ /* LOCK */
+ ret = pthread_mutex_trylock(session->ti_lock);
+ if (!ret) {
+ /* be extra careful, someone could have been faster */
+ if (*session->ti_inuse) {
+ pthread_mutex_unlock(session->ti_lock);
+ return 0;
+ }
}
} else { /* timeout == -1 */
- ret = pthread_mutex_lock(lock);
+ /* LOCK */
+ ret = pthread_mutex_lock(session->ti_lock);
+ if (!ret) {
+ while (*session->ti_inuse) {
+ ret = pthread_cond_wait(session->ti_cond, session->ti_lock);
+ if (ret) {
+ pthread_mutex_unlock(session->ti_lock);
+ break;
+ }
+ }
+ }
}
- if (ret == ETIMEDOUT) {
- /* timeout */
- return 0;
- } else if (ret) {
+ if (ret) {
+ if ((ret == EBUSY) || (ret == ETIMEDOUT)) {
+ /* timeout */
+ return 0;
+ }
+
/* error */
- ERR("Mutex lock failed (%s, %s).", func, strerror(ret));
+ ERR("%s: failed to lock a session (%s).", func, strerror(ret));
return -1;
}
/* ok */
+ assert(*session->ti_inuse == 0);
+ *session->ti_inuse = 1;
+
+ /* UNLOCK */
+ ret = pthread_mutex_unlock(session->ti_lock);
+ if (ret) {
+ /* error */
+ ERR("%s: faile to unlock a session (%s).", func, strerror(ret));
+ return -1;
+ }
+
+ return 1;
+}
+
+int
+nc_session_unlock(struct nc_session *session, int timeout, const char *func)
+{
+ int ret;
+ struct timespec ts_timeout;
+
+ assert(*session->ti_inuse);
+
+ if (timeout > 0) {
+ nc_gettimespec(&ts_timeout);
+
+ ts_timeout.tv_sec += timeout / 1000;
+ ts_timeout.tv_nsec += (timeout % 1000) * 1000000;
+
+ /* LOCK */
+ ret = pthread_mutex_timedlock(session->ti_lock, &ts_timeout);
+ } else if (!timeout) {
+ /* LOCK */
+ ret = pthread_mutex_trylock(session->ti_lock);
+ } else { /* timeout == -1 */
+ /* LOCK */
+ ret = pthread_mutex_lock(session->ti_lock);
+ }
+
+ if (ret && (ret != EBUSY) && (ret != ETIMEDOUT)) {
+ /* error */
+ ERR("%s: failed to lock a session (%s).", func, strerror(ret));
+ return -1;
+ } else if (ret) {
+ WRN("%s: session lock timeout, should not happen.");
+ }
+
+ *session->ti_inuse = 0;
+ pthread_cond_signal(session->ti_cond);
+
+ if (!ret) {
+ /* UNLOCK */
+ ret = pthread_mutex_unlock(session->ti_lock);
+ if (ret) {
+ /* error */
+ ERR("%s: failed to unlock a session (%s).", func, strerror(ret));
+ return -1;
+ }
+ }
+
return 1;
}
@@ -339,7 +455,7 @@
}
if (session->ti_lock) {
- r = nc_timedlock(session->ti_lock, NC_SESSION_FREE_LOCK_TIMEOUT, __func__);
+ r = nc_session_lock(session, NC_SESSION_FREE_LOCK_TIMEOUT, __func__);
if (r == -1) {
return;
} else if (!r) {
@@ -535,11 +651,14 @@
/* final cleanup */
if (session->ti_lock) {
if (locked) {
- pthread_mutex_unlock(session->ti_lock);
+ nc_session_unlock(session, NC_SESSION_LOCK_TIMEOUT, __func__);
}
if (!multisession) {
pthread_mutex_destroy(session->ti_lock);
+ pthread_cond_destroy(session->ti_cond);
free(session->ti_lock);
+ free(session->ti_cond);
+ free((int *)session->ti_inuse);
}
}