Skip to content

Commit a6f8979

Browse files
committed
Add support for recursive locks and try_lock
1 parent 10483a6 commit a6f8979

File tree

6 files changed

+146
-2
lines changed

6 files changed

+146
-2
lines changed

include/qt_addrstat.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ static QINLINE qthread_addrstat_t *qthread_addrstat_new(void)
1818
ret->FEQ = NULL;
1919
ret->FFQ = NULL;
2020
ret->FFWQ = NULL;
21+
ret->acq_owner_stat.state = -1;
22+
ret->acq_owner_stat.recursive_access_counter = 0;
2123
QTHREAD_EMPTY_TIMER_INIT(ret);
2224
}
2325
return ret;

include/qt_blocking_structs.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include "qt_profiling.h"
1010
#include "qt_debug.h"
1111

12+
#define FEB_is_recursive_lock 0
13+
1214
typedef enum blocking_syscalls {
1315
ACCEPT,
1416
CONNECT,
@@ -46,6 +48,11 @@ typedef struct _qt_blocking_queue_node_s {
4648
int err;
4749
} qt_blocking_queue_node_t;
4850

51+
typedef struct qthread_acquire_owner_stat_s {
52+
int_fast8_t state;
53+
int_fast8_t recursive_access_counter;
54+
} qthread_acquire_owner_stat_t;
55+
4956
typedef struct qthread_addrstat_s {
5057
QTHREAD_FASTLOCK_TYPE lock;
5158
qthread_addrres_t *EFQ;
@@ -57,6 +64,8 @@ typedef struct qthread_addrstat_s {
5764
#endif
5865
uint_fast8_t full;
5966
uint_fast8_t valid;
67+
qthread_acquire_owner_stat_t acq_owner_stat;
68+
6069
} qthread_addrstat_t;
6170

6271
#ifdef UNPOOLED

include/qt_feb.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ typedef filter_code (*qt_feb_taskfilter_f)(qt_key_t addr,
1717
qthread_t *restrict waiter,
1818
void *restrict arg);
1919

20+
int INTERNAL qthread_feb_adr_init(const aligned_t *dest, const bool is_from_recursive_lock);
21+
2022
void INTERNAL qt_feb_subsystem_init(uint_fast8_t);
2123

2224
int API_FUNC qthread_writeEF_nb(aligned_t *restrict const dest,

include/qthread/qthread.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "common.h" /* important configuration options */
99

1010
#include <string.h> /* for memcpy() */
11+
#include <stdbool.h> /* for bool */
1112

1213
#ifndef QTHREAD_NOALIGNCHECK
1314
# include <stdio.h> /* for fprintf() */
@@ -124,7 +125,6 @@ typedef struct _syncvar_s {
124125
typedef unsigned short qthread_shepherd_id_t;
125126
typedef unsigned short qthread_worker_id_t;
126127

127-
128128
/* for convenient arguments to qthread_fork */
129129
typedef aligned_t (*qthread_f)(void *arg);
130130
typedef void (*qthread_agg_f)(int count, qthread_f *f, void **arg, void **ret, uint16_t flags);
@@ -434,6 +434,7 @@ int qthread_syncvar_status(syncvar_t *const v);
434434
int qthread_empty(const aligned_t *dest);
435435
int qthread_syncvar_empty(syncvar_t *restrict dest);
436436
int qthread_fill(const aligned_t *dest);
437+
int qthread_fill_recursive(const aligned_t *dest);
437438
int qthread_syncvar_fill(syncvar_t *restrict dest);
438439

439440
/* These functions wait for memory to become empty, and then fill it. When
@@ -553,6 +554,10 @@ int qthread_readXX(aligned_t *dest,
553554
*/
554555
int qthread_lock(const aligned_t *a);
555556
int qthread_unlock(const aligned_t *a);
557+
int qthread_lock_init(const aligned_t *a, const bool is_recursive);
558+
559+
const int qthread_trylock(const aligned_t *a);
560+
556561

557562
#if defined(QTHREAD_MUTEX_INCREMENT) || \
558563
(QTHREAD_ASSEMBLY_ARCH == QTHREAD_POWERPC32) || \

src/feb.c

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,74 @@ int API_FUNC qthread_empty(const aligned_t *dest)
665665
return QTHREAD_SUCCESS;
666666
} /*}}} */
667667

668+
int INTERNAL qthread_feb_adr_init(const aligned_t *dest, const bool is_from_recursive_lock)
669+
{ /*{{{ */
670+
const aligned_t *alignedaddr;
671+
672+
if (qlib == NULL) {
673+
return QTHREAD_SUCCESS;
674+
}
675+
qthread_addrstat_t *m;
676+
const int lockbin = QTHREAD_CHOOSE_STRIPE2(dest);
677+
qthread_shepherd_t *shep = qthread_internal_getshep();
678+
679+
assert(qthread_library_initialized);
680+
681+
if (!shep) {
682+
return qthread_feb_blocker_func((void *)dest, NULL, FILL/*!!!!!!!XXX!!!*/);
683+
}
684+
qthread_debug(FEB_CALLS, "dest=%p (tid=%i)\n", dest, qthread_id());
685+
QALIGN(dest, alignedaddr);
686+
/* lock hash */
687+
QTHREAD_COUNT_THREADS_BINCOUNTER(febs, lockbin);
688+
#ifdef LOCK_FREE_FEBS
689+
do {
690+
m = qt_hash_get(FEBs[lockbin], (void *)alignedaddr);
691+
if (m) {
692+
return QTHREAD_NOT_ALLOWED;
693+
} else {
694+
if(is_from_recursive_lock) {
695+
m = qthread_addrstat_new();
696+
if (!m) { return QTHREAD_MALLOC_ERROR; }
697+
m->full = 1;
698+
m->acq_owner_stat.state = FEB_is_recursive_lock;
699+
MACHINE_FENCE;
700+
701+
if (!qt_hash_put(FEBs[lockbin], (void *)alignedaddr, m)) {
702+
qthread_addrstat_delete(m);
703+
continue;
704+
}
705+
}
706+
/* already full */
707+
break;
708+
}
709+
} while (1);
710+
#else /* ifdef LOCK_FREE_FEBS */
711+
qt_hash_lock(FEBs[lockbin]);
712+
{ /* BEGIN CRITICAL SECTION */
713+
m = (qthread_addrstat_t *)qt_hash_get_locked(FEBs[lockbin], (void *)alignedaddr);
714+
if (m) {
715+
return QTHREAD_NOT_ALLOWED;
716+
} else {
717+
if(is_from_recursive_lock) {
718+
m = qthread_addrstat_new();
719+
if (!m) { return QTHREAD_MALLOC_ERROR; }
720+
m->full = 1;
721+
m->acq_owner_stat.state = FEB_is_recursive_lock;
722+
MACHINE_FENCE;
723+
724+
if (!qt_hash_put_locked(FEBs[lockbin], (void *)alignedaddr, m)){
725+
qthread_addrstat_delete(m);
726+
return QTHREAD_MALLOC_ERROR;
727+
}
728+
}
729+
}
730+
} /* END CRITICAL SECTION */
731+
qt_hash_unlock(FEBs[lockbin]); /* unlock hash */
732+
#endif /* ifdef LOCK_FREE_FEBS */
733+
return QTHREAD_SUCCESS;
734+
} /*}}} */
735+
668736
int API_FUNC qthread_fill(const aligned_t *dest)
669737
{ /*{{{ */
670738
const aligned_t *alignedaddr;
@@ -708,11 +776,22 @@ int API_FUNC qthread_fill(const aligned_t *dest)
708776
m = (qthread_addrstat_t *)qt_hash_get_locked(FEBs[lockbin], (void *)alignedaddr);
709777
if (m) {
710778
QTHREAD_FASTLOCK_LOCK(&m->lock);
779+
if(m->acq_owner_stat.state >= FEB_is_recursive_lock) {
780+
--m->acq_owner_stat.recursive_access_counter;
781+
}
711782
}
712783
} /* END CRITICAL SECTION */
713784
qt_hash_unlock(FEBs[lockbin]); /* unlock hash */
714785
#endif /* ifdef LOCK_FREE_FEBS */
715786
if (m) {
787+
if(m->acq_owner_stat.state >= FEB_is_recursive_lock) {
788+
--m->acq_owner_stat.recursive_access_counter;
789+
if (m->acq_owner_stat.recursive_access_counter >0){
790+
qthread_debug(FEB_BEHAVIOR, "dest=%p (tid=%i): decrementing recursive_access_counter\n", dest, qthread_id());
791+
return QTHREAD_SUCCESS;
792+
}
793+
m->acq_owner_stat.state = FEB_is_recursive_lock; //reset
794+
}
716795
/* if dest wasn't in the hash, it was already full. Since it was,
717796
* we need to fill it. */
718797
qthread_debug(FEB_BEHAVIOR, "dest=%p (tid=%i): filling, maybe alerting waiters\n", dest, qthread_id());
@@ -785,6 +864,7 @@ int API_FUNC qthread_writeF(aligned_t *restrict dest,
785864
return QTHREAD_SUCCESS;
786865
} /*}}} */
787866

867+
788868
int API_FUNC qthread_writeF_const(aligned_t *dest,
789869
aligned_t src)
790870
{ /*{{{ */
@@ -1357,6 +1437,7 @@ int API_FUNC qthread_readFF_nb(aligned_t *restrict dest,
13571437
return QTHREAD_SUCCESS;
13581438
} /*}}} */
13591439

1440+
13601441
/* the way this works is that:
13611442
* 1 - src's FEB state must be "full"
13621443
* 2 - data is copied from src to destination
@@ -1436,7 +1517,17 @@ int API_FUNC qthread_readFE(aligned_t *restrict dest,
14361517
assert(m);
14371518
qthread_debug(FEB_DETAILS, "data structure locked\n");
14381519
/* by this point m is locked */
1439-
if (m->full == 0) { /* empty, thus, we must block */
1520+
if (m->full == 0) { /* empty, thus, we must block */
1521+
/* unless read_FE was taken by qthread_lock() */
1522+
/* on same thread */
1523+
if(m->acq_owner_stat.state >= FEB_is_recursive_lock) {
1524+
if (m->acq_owner_stat.state == qthread_id()) {
1525+
++m->acq_owner_stat.recursive_access_counter;
1526+
QTHREAD_FASTLOCK_UNLOCK(&m->lock);
1527+
QTHREAD_FEB_TIMER_STOP(febblock, me);
1528+
return QTHREAD_SUCCESS;
1529+
}
1530+
}
14401531
QTHREAD_WAIT_TIMER_DECLARATION;
14411532
qthread_addrres_t *X = ALLOC_ADDRRES();
14421533

@@ -1465,13 +1556,22 @@ int API_FUNC qthread_readFE(aligned_t *restrict dest,
14651556
*(aligned_t *)dest = *(aligned_t *)src;
14661557
MACHINE_FENCE;
14671558
}
1559+
1560+
if(m->acq_owner_stat.state >= FEB_is_recursive_lock) {
1561+
m->acq_owner_stat.state == qthread_id();
1562+
++m->acq_owner_stat.recursive_access_counter;
1563+
MACHINE_FENCE;
1564+
qthread_debug(FEB_BEHAVIOR, "dest=%p (tid=%i): incrementing recursive_access_counter\n", dest, qthread_id());
1565+
}
1566+
14681567
qthread_debug(FEB_BEHAVIOR, "tid %u succeeded on %p=%p\n", me->thread_id, dest, src);
14691568
qthread_gotlock_empty(me->rdata->shepherd_ptr, m, (void *)alignedaddr);
14701569
}
14711570
QTHREAD_FEB_TIMER_STOP(febblock, me);
14721571
return QTHREAD_SUCCESS;
14731572
} /*}}} */
14741573

1574+
14751575
/* the way this works is that:
14761576
* 1 - src's FEB state is ignored
14771577
* 2 - data is copied from src to destination
@@ -1552,6 +1652,13 @@ int API_FUNC qthread_readFE_nb(aligned_t *restrict dest,
15521652
qthread_debug(FEB_DETAILS, "data structure locked\n");
15531653
/* by this point m is locked */
15541654
if (m->full == 0) { /* empty, thus, we must fail */
1655+
/* unless called from qthread_trylock on a recursive lock*/
1656+
if(m->acq_owner_stat.state >= FEB_is_recursive_lock) {
1657+
1658+
++m->acq_owner_stat.recursive_access_counter;
1659+
QTHREAD_FASTLOCK_UNLOCK(&m->lock);
1660+
return QTHREAD_SUCCESS;
1661+
}
15551662
qthread_debug(FEB_BEHAVIOR, "tid %u non-blocking fail\n", me->thread_id);
15561663
QTHREAD_FASTLOCK_UNLOCK(&m->lock);
15571664
return QTHREAD_OPFAIL;
@@ -1560,6 +1667,14 @@ int API_FUNC qthread_readFE_nb(aligned_t *restrict dest,
15601667
*(aligned_t *)dest = *(aligned_t *)src;
15611668
MACHINE_FENCE;
15621669
}
1670+
1671+
if(m->acq_owner_stat.state >= FEB_is_recursive_lock) {
1672+
m->acq_owner_stat.state == qthread_id();
1673+
++m->acq_owner_stat.recursive_access_counter;
1674+
MACHINE_FENCE;
1675+
qthread_debug(FEB_BEHAVIOR, "dest=%p (tid=%i): incrementing recursive_access_counter\n", dest, qthread_id());
1676+
}
1677+
15631678
qthread_debug(FEB_BEHAVIOR, "tid %u succeeded on %p=%p\n", me->thread_id, dest, src);
15641679
qthread_gotlock_empty(me->rdata->shepherd_ptr, m, (void *)alignedaddr);
15651680
}

src/locks.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,28 @@
44

55
/* The API */
66
#include "qthread/qthread.h"
7+
#include "qt_feb.h"
78

89
/* Internal Headers */
910
#include "qt_visibility.h"
1011

1112
/* functions to implement FEB-ish locking/unlocking*/
1213

14+
int API_FUNC qthread_lock_init(const aligned_t *a, const bool is_recursive)
15+
{ /*{{{ */
16+
return qthread_feb_adr_init(a, is_recursive);
17+
} /*}}} */
18+
1319
int API_FUNC qthread_lock(const aligned_t *a)
1420
{ /*{{{ */
1521
return qthread_readFE(NULL, a);
1622
} /*}}} */
1723

24+
const int API_FUNC qthread_trylock(const aligned_t *a)
25+
{ /*{{{ */
26+
return qthread_readFE_nb(NULL, a);
27+
} /*}}} */
28+
1829
int API_FUNC qthread_unlock(const aligned_t *a)
1930
{ /*{{{ */
2031
return qthread_fill(a);

0 commit comments

Comments
 (0)