Skip to content

Commit 01130bd

Browse files
committed
session FEATURE session msgs lock with timeout
Otherwise the timeout can easily be ignored and used only after the msgs lock is held, which can take some time on its own.
1 parent 217bac6 commit 01130bd

3 files changed

Lines changed: 135 additions & 20 deletions

File tree

src/session.c

Lines changed: 77 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -344,11 +344,6 @@ nc_session_rpc_unlock(struct nc_session *session, int timeout, const char *func)
344344
return 1;
345345
}
346346

347-
/*
348-
* @return 1 - success
349-
* 0 - timeout
350-
* -1 - error
351-
*/
352347
int
353348
nc_session_io_lock(struct nc_session *session, int timeout, const char *func)
354349
{
@@ -395,6 +390,66 @@ nc_session_io_unlock(struct nc_session *session, const char *func)
395390
return 1;
396391
}
397392

393+
int
394+
nc_session_client_msgs_lock(struct nc_session *session, int *timeout, const char *func)
395+
{
396+
int ret;
397+
int32_t diff_msec;
398+
struct timespec ts_timeout, ts_start, ts_end;
399+
400+
assert(session->side == NC_CLIENT);
401+
402+
if (*timeout > 0) {
403+
/* get current time */
404+
nc_gettimespec_real(&ts_start);
405+
406+
nc_gettimespec_real(&ts_timeout);
407+
nc_addtimespec(&ts_timeout, *timeout);
408+
409+
ret = pthread_mutex_timedlock(&session->opts.client.msgs_lock, &ts_timeout);
410+
if (!ret) {
411+
/* update timeout based on what was elapsed */
412+
nc_gettimespec_real(&ts_end);
413+
diff_msec = nc_difftimespec(&ts_start, &ts_end);
414+
*timeout -= diff_msec;
415+
}
416+
} else if (!*timeout) {
417+
ret = pthread_mutex_trylock(&session->opts.client.msgs_lock);
418+
} else { /* timeout == -1 */
419+
ret = pthread_mutex_lock(&session->opts.client.msgs_lock);
420+
}
421+
422+
if (ret) {
423+
if ((ret == EBUSY) || (ret == ETIMEDOUT)) {
424+
/* timeout */
425+
return 0;
426+
}
427+
428+
/* error */
429+
ERR(session, "%s: failed to MSGS lock a session (%s).", func, strerror(ret));
430+
return -1;
431+
}
432+
433+
return 1;
434+
}
435+
436+
int
437+
nc_session_client_msgs_unlock(struct nc_session *session, const char *func)
438+
{
439+
int ret;
440+
441+
assert(session->side == NC_CLIENT);
442+
443+
ret = pthread_mutex_unlock(&session->opts.client.msgs_lock);
444+
if (ret) {
445+
/* error */
446+
ERR(session, "%s: failed to MSGS unlock a session (%s).", func, strerror(ret));
447+
return -1;
448+
}
449+
450+
return 1;
451+
}
452+
398453
API NC_STATUS
399454
nc_session_get_status(const struct nc_session *session)
400455
{
@@ -555,7 +610,7 @@ nc_send_msg_io(struct nc_session *session, int io_timeout, struct lyd_node *op)
555610
API void
556611
nc_session_free(struct nc_session *session, void (*data_free)(void *))
557612
{
558-
int r, i, rpc_locked = 0, sock = -1;
613+
int r, i, rpc_locked = 0, msgs_locked = 0, sock = -1, timeout;
559614
int connected; /* flag to indicate whether the transport socket is still connected */
560615
int multisession = 0; /* flag for more NETCONF sessions on a single SSH session */
561616
struct nc_session *siter;
@@ -601,10 +656,20 @@ nc_session_free(struct nc_session *session, void (*data_free)(void *))
601656
}
602657

603658
if (session->side == NC_CLIENT) {
659+
timeout = NC_SESSION_FREE_LOCK_TIMEOUT;
660+
604661
/* MSGS LOCK */
605-
pthread_mutex_lock(&session->opts.client.msgs_lock);
662+
r = nc_session_client_msgs_lock(session, &timeout, __func__);
663+
if (r == -1) {
664+
return;
665+
} else if (r) {
666+
msgs_locked = 1;
667+
} else {
668+
/* else failed to lock it, too bad */
669+
ERR(session, "Freeing a session while messages are being received.");
670+
}
606671

607-
/* cleanup message queue*/
672+
/* cleanup message queue */
608673
for (contiter = session->opts.client.msgs; contiter; ) {
609674
ly_in_free(contiter->msg, 1);
610675

@@ -613,8 +678,10 @@ nc_session_free(struct nc_session *session, void (*data_free)(void *))
613678
free(p);
614679
}
615680

616-
/* MSGS UNLOCK */
617-
pthread_mutex_unlock(&session->opts.client.msgs_lock);
681+
if (msgs_locked) {
682+
/* MSGS UNLOCK */
683+
nc_session_client_msgs_unlock(session, __func__);
684+
}
618685

619686
/* receive any leftover messages */
620687
while (nc_read_msg_poll_io(session, 0, &msg) == 1) {

src/session_client.c

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,11 +1744,18 @@ recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct l
17441744
*message = NULL;
17451745

17461746
/* MSGS LOCK */
1747-
pthread_mutex_lock(&session->opts.client.msgs_lock);
1747+
r = nc_session_client_msgs_lock(session, &timeout, __func__);
1748+
if (!r) {
1749+
ret = NC_MSG_WOULDBLOCK;
1750+
goto cleanup;
1751+
} else if (r == -1) {
1752+
ret = NC_MSG_ERROR;
1753+
goto cleanup;
1754+
}
17481755

17491756
/* Find the expected message in the buffer */
17501757
prev = NULL;
1751-
for (cont = session->opts.client.msgs; cont && cont->type != expected; cont = cont->next) {
1758+
for (cont = session->opts.client.msgs; cont && (cont->type != expected); cont = cont->next) {
17521759
prev = cont;
17531760
}
17541761

@@ -1764,23 +1771,23 @@ recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct l
17641771
ret = cont->type;
17651772
msg = cont->msg;
17661773
free(cont);
1767-
goto cleanup;
1774+
goto cleanup_unlock;
17681775
}
17691776

17701777
/* Read a message from the wire */
17711778
r = nc_read_msg_poll_io(session, timeout, &msg);
17721779
if (!r) {
17731780
ret = NC_MSG_WOULDBLOCK;
1774-
goto cleanup;
1781+
goto cleanup_unlock;
17751782
} else if (r == -1) {
17761783
ret = NC_MSG_ERROR;
1777-
goto cleanup;
1784+
goto cleanup_unlock;
17781785
}
17791786

17801787
/* Basic check to determine message type */
17811788
ret = get_msg_type(session, msg);
17821789
if (ret == NC_MSG_ERROR) {
1783-
goto cleanup;
1790+
goto cleanup_unlock;
17841791
}
17851792

17861793
/* If received a message of different type store it in the buffer */
@@ -1793,18 +1800,19 @@ recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct l
17931800
if (!*cont_ptr) {
17941801
ERRMEM;
17951802
ret = NC_MSG_ERROR;
1796-
goto cleanup;
1803+
goto cleanup_unlock;
17971804
}
17981805
(*cont_ptr)->msg = msg;
1799-
(*cont_ptr)->type = ret;
18001806
msg = NULL;
1807+
(*cont_ptr)->type = ret;
18011808
(*cont_ptr)->next = NULL;
18021809
}
18031810

1804-
cleanup:
1811+
cleanup_unlock:
18051812
/* MSGS UNLOCK */
1806-
pthread_mutex_unlock(&session->opts.client.msgs_lock);
1813+
nc_session_client_msgs_unlock(session, __func__);
18071814

1815+
cleanup:
18081816
if (ret == expected) {
18091817
*message = msg;
18101818
} else {

src/session_p.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,10 +534,50 @@ int nc_session_rpc_lock(struct nc_session *session, int timeout, const char *fun
534534

535535
int nc_session_rpc_unlock(struct nc_session *session, int timeout, const char *func);
536536

537+
/**
538+
* @brief Lock IO lock on a session.
539+
*
540+
* @param[in] session Session to lock.
541+
* @param[in] timeout Timeout in msec to use.
542+
* @param[in] func Caller function for logging.
543+
* @return 1 on success;
544+
* @return 0 on timeout;
545+
* @return -1 on error.
546+
*/
537547
int nc_session_io_lock(struct nc_session *session, int timeout, const char *func);
538548

549+
/**
550+
* @brief Unlock IO lock on a session.
551+
*
552+
* @param[in] session Session to unlock.
553+
* @param[in] func Caller function for logging.
554+
* @return 1 on success;
555+
* @return -1 on error.
556+
*/
539557
int nc_session_io_unlock(struct nc_session *session, const char *func);
540558

559+
/**
560+
* @brief Lock MSGS lock on a session.
561+
*
562+
* @param[in] session Session to lock.
563+
* @param[in,out] timeout Timeout in msec to use. If positive and on successful lock, is updated based on what was elapsed.
564+
* @param[in] func Caller function for logging.
565+
* @return 1 on success;
566+
* @return 0 on timeout;
567+
* @return -1 on error.
568+
*/
569+
int nc_session_client_msgs_lock(struct nc_session *session, int *timeout, const char *func);
570+
571+
/**
572+
* @brief Unlock MSGS lock on a session.
573+
*
574+
* @param[in] session Session to unlock.
575+
* @param[in] func Caller function for logging.
576+
* @return 1 on success;
577+
* @return -1 on error.
578+
*/
579+
int nc_session_client_msgs_unlock(struct nc_session *session, const char *func);
580+
541581
int nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func);
542582

543583
int nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func);

0 commit comments

Comments
 (0)