Skip to content

Commit 38258e1

Browse files
authored
Generalize MAX_THREADS (#636)
Enable any number of MAX_THREADS
1 parent 525299b commit 38258e1

File tree

3 files changed

+52
-137
lines changed

3 files changed

+52
-137
lines changed

src/task.c

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,19 @@ _Static_assert((ARRAY_SIZE(task_type_name) == NUM_TASK_TYPES),
2323
static void
2424
task_init_tid_bitmask(uint64 *tid_bitmask)
2525
{
26-
// We use a 64 bit word to act as the thread bitmap.
27-
_Static_assert(MAX_THREADS == 64, "Max threads should be 64");
2826
/*
2927
* This is a special bitmask where 1 indicates free and 0 indicates
3028
* allocated. So, we set all bits to 1 during init.
3129
*/
3230
for (int i = 0; i < MAX_THREADS; i++) {
33-
*tid_bitmask |= (1ULL << i);
31+
tid_bitmask[i / 64] |= (1ULL << (i % 64));
3432
}
3533
}
3634

3735
static inline uint64 *
3836
task_system_get_tid_bitmask(task_system *ts)
3937
{
40-
return &ts->tid_bitmask;
38+
return ts->tid_bitmask;
4139
}
4240

4341
static threadid *
@@ -46,15 +44,6 @@ task_system_get_max_tid(task_system *ts)
4644
return &ts->max_tid;
4745
}
4846

49-
/*
50-
* Return the bitmasks of tasks active. Mainly intended as a testing hook.
51-
*/
52-
uint64
53-
task_active_tasks_mask(task_system *ts)
54-
{
55-
return *task_system_get_tid_bitmask(ts);
56-
}
57-
5847
/*
5948
* Allocate a threadid. Returns INVALID_TID when no tid is available.
6049
*/
@@ -66,22 +55,42 @@ task_allocate_threadid(task_system *ts)
6655
uint64 old_bitmask;
6756
uint64 new_bitmask;
6857

69-
do {
70-
old_bitmask = *tid_bitmask;
58+
while (!__sync_lock_test_and_set(&ts->tid_bitmask_lock, 1)) {
59+
// spin
60+
}
61+
62+
int i;
63+
for (i = 0; i < (MAX_THREADS + 63) / 64; i++) {
64+
old_bitmask = tid_bitmask[i];
7165
// first bit set to 1 starting from LSB.
7266
uint64 pos = __builtin_ffsl(old_bitmask);
7367

7468
// If all threads are in-use, bitmask will be all 0s.
7569
if (pos == 0) {
76-
return INVALID_TID;
70+
continue;
7771
}
7872

7973
// builtin_ffsl returns the position plus 1.
8074
tid = pos - 1;
8175
// set bit at that position to 0, indicating in use.
8276
new_bitmask = (old_bitmask & ~(1ULL << (pos - 1)));
83-
} while (
84-
!__sync_bool_compare_and_swap(tid_bitmask, old_bitmask, new_bitmask));
77+
if (__sync_bool_compare_and_swap(
78+
&tid_bitmask[i], old_bitmask, new_bitmask))
79+
{
80+
break;
81+
}
82+
}
83+
84+
__sync_lock_release(&ts->tid_bitmask_lock);
85+
86+
if (i == 2) {
87+
return INVALID_TID;
88+
}
89+
90+
tid += i * 64;
91+
if (tid >= MAX_THREADS) {
92+
return INVALID_TID;
93+
}
8594

8695
// Invariant: we have successfully allocated tid
8796

@@ -103,20 +112,22 @@ task_deallocate_threadid(task_system *ts, threadid tid)
103112
{
104113
uint64 *tid_bitmask = task_system_get_tid_bitmask(ts);
105114

106-
uint64 bitmask_val = *tid_bitmask;
115+
uint64 bitmask_val = tid_bitmask[tid / 64];
107116

108117
// Ensure that caller is only clearing for a thread that's in-use.
109-
platform_assert(!(bitmask_val & (1ULL << tid)),
118+
platform_assert(!(bitmask_val & (1ULL << (tid % 64))),
110119
"Thread [%lu] is expected to be in-use. Bitmap: 0x%lx",
111120
tid,
112121
bitmask_val);
113122

114123
// set bit back to 1 to indicate a free slot.
115-
uint64 tmp_bitmask = *tid_bitmask;
116-
uint64 new_value = tmp_bitmask | (1ULL << tid);
117-
while (!__sync_bool_compare_and_swap(tid_bitmask, tmp_bitmask, new_value)) {
118-
tmp_bitmask = *tid_bitmask;
119-
new_value = tmp_bitmask | (1ULL << tid);
124+
uint64 tmp_bitmask = tid_bitmask[tid / 64];
125+
uint64 new_value = tmp_bitmask | (1ULL << (tid % 64));
126+
while (!__sync_bool_compare_and_swap(
127+
tid_bitmask + tid / 64, tmp_bitmask, new_value))
128+
{
129+
tmp_bitmask = tid_bitmask[tid / 64];
130+
new_value = tmp_bitmask | (1ULL << (tid % 64));
120131
}
121132
}
122133

@@ -877,10 +888,11 @@ task_system_create(platform_heap_id hid,
877888
*system = NULL;
878889
return STATUS_NO_MEMORY;
879890
}
880-
ts->cfg = cfg;
881-
ts->ioh = ioh;
882-
ts->heap_id = hid;
883-
task_init_tid_bitmask(&ts->tid_bitmask);
891+
ts->cfg = cfg;
892+
ts->ioh = ioh;
893+
ts->heap_id = hid;
894+
ts->tid_bitmask_lock = 0;
895+
task_init_tid_bitmask(ts->tid_bitmask);
884896

885897
// task initialization
886898
register_standard_hooks(ts);
@@ -935,12 +947,14 @@ task_system_destroy(platform_heap_id hid, task_system **ts_in)
935947
if (tid != INVALID_TID) {
936948
task_deregister_this_thread(ts);
937949
}
938-
if (ts->tid_bitmask != ((uint64)-1)) {
939-
platform_error_log(
950+
for (int i = 0; i < ARRAY_SIZE(ts->tid_bitmask); i++) {
951+
platform_assert(
952+
ts->tid_bitmask[i] == ((uint64)-1),
940953
"Destroying task system that still has some registered threads."
941-
", tid=%lu, tid_bitmask=0x%lx\n",
954+
", tid=%lu, tid_bitmask[%d] = %lx\n",
942955
tid,
943-
ts->tid_bitmask);
956+
i,
957+
ts->tid_bitmask[i]);
944958
}
945959
platform_free(hid, ts);
946960
*ts_in = (task_system *)NULL;

src/task.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,13 @@ struct task_system {
112112
platform_io_handle *ioh;
113113
platform_heap_id heap_id;
114114
/*
115-
* bitmask used for generating and clearing thread id's.
115+
* bitmask used for allocating thread id's.
116116
* If a bit is set to 0, it means we have an in use thread id for that
117117
* particular position, 1 means it is unset and that thread id is available
118118
* for use.
119119
*/
120-
uint64 tid_bitmask;
120+
uint64 tid_bitmask_lock;
121+
uint64 tid_bitmask[(MAX_THREADS + 63) / 64];
121122
// max thread id so far.
122123
threadid max_tid;
123124
void *thread_scratch[MAX_THREADS];
@@ -270,8 +271,5 @@ task_wait_for_completion(task_system *ts);
270271
threadid
271272
task_get_max_tid(task_system *ts);
272273

273-
uint64
274-
task_active_tasks_mask(task_system *ts);
275-
276274
void
277275
task_print_stats(task_system *ts);

tests/unit/task_system_test.c

Lines changed: 2 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ typedef struct {
3838
task_system *tasks;
3939
platform_thread this_thread_id; // OS-generated thread ID
4040
threadid exp_thread_idx; // Splinter-generated expected thread index
41-
uint64 active_threads_bitmask;
4241
} thread_config;
4342

4443
// Configuration for worker threads used in lock-step testing exercise
@@ -89,8 +88,6 @@ CTEST_DATA(task_system)
8988
// Following get setup pointing to allocated memory
9089
platform_io_handle *ioh; // Only prerequisite needed to setup task system
9190
task_system *tasks;
92-
93-
uint64 active_threads_bitmask;
9491
};
9592

9693
/*
@@ -138,22 +135,6 @@ CTEST_SETUP(task_system)
138135

139136
// Main task (this thread) is at index 0
140137
ASSERT_EQUAL(0, platform_get_tid());
141-
142-
// Main thread should now be marked as being active in the bitmask.
143-
// Active threads have their bit turned -OFF- in this bitmask.
144-
uint64 task_bitmask = task_active_tasks_mask(data->tasks);
145-
uint64 all_threads_inactive_mask = (~0L);
146-
uint64 this_thread_active_mask = (~0x1L);
147-
uint64 exp_bitmask = (all_threads_inactive_mask & this_thread_active_mask);
148-
149-
ASSERT_EQUAL(exp_bitmask,
150-
task_bitmask,
151-
"exp_bitmask=0x%x, task_bitmask=0x%x\n",
152-
exp_bitmask,
153-
task_bitmask);
154-
155-
// Save it off, so it can be used for verification in a test case.
156-
data->active_threads_bitmask = exp_bitmask;
157138
}
158139

159140
// Teardown function for suite, called after every test in suite
@@ -209,8 +190,7 @@ CTEST2(task_system, test_one_thread_using_lower_apis)
209190
thread_cfg.tasks = data->tasks;
210191

211192
// Main thread is at index 0
212-
thread_cfg.exp_thread_idx = 1;
213-
thread_cfg.active_threads_bitmask = task_active_tasks_mask(data->tasks);
193+
thread_cfg.exp_thread_idx = 1;
214194

215195
platform_status rc = STATUS_OK;
216196

@@ -264,8 +244,7 @@ CTEST2(task_system, test_one_thread_using_extern_apis)
264244
thread_cfg.tasks = data->tasks;
265245

266246
// Main thread is at index 0
267-
thread_cfg.exp_thread_idx = 1;
268-
thread_cfg.active_threads_bitmask = task_active_tasks_mask(data->tasks);
247+
thread_cfg.exp_thread_idx = 1;
269248

270249
platform_status rc = STATUS_OK;
271250

@@ -363,26 +342,6 @@ CTEST2(task_system, test_task_system_creation_with_bg_threads)
363342
task_system_destroy(data->hid, &data->tasks);
364343
platform_status rc = create_task_system_with_bg_threads(data, 2, 4);
365344
ASSERT_TRUE(SUCCESS(rc));
366-
367-
uint64 all_threads_inactive_mask = (~0L);
368-
369-
// Construct known bit-mask for active threads knowing that the background
370-
// threads are started up when task system is created w/bg threads.
371-
threadid max_thread_id = task_get_max_tid(data->tasks);
372-
uint64 active_threads_mask = 0;
373-
for (int tid = 0; tid < max_thread_id; tid++) {
374-
active_threads_mask |= (1L << tid);
375-
}
376-
active_threads_mask = ~active_threads_mask;
377-
378-
uint64 exp_bitmask = (all_threads_inactive_mask & active_threads_mask);
379-
uint64 task_bitmask = task_active_tasks_mask(data->tasks);
380-
381-
ASSERT_EQUAL(exp_bitmask,
382-
task_bitmask,
383-
"exp_bitmask=0x%x, task_bitmask=0x%x\n",
384-
exp_bitmask,
385-
task_bitmask);
386345
}
387346

388347
/*
@@ -533,52 +492,18 @@ exec_one_thread_use_lower_apis(void *arg)
533492
{
534493
thread_config *thread_cfg = (thread_config *)arg;
535494

536-
uint64 task_bitmask_before_register =
537-
task_active_tasks_mask(thread_cfg->tasks);
538-
539-
// Verify that the state of active-threads bitmask (managed by Splinter) has
540-
// not changed just by creating this pthread. It should be the same as what
541-
// we had recorded just prior to platform_thread_create().
542-
ASSERT_EQUAL(thread_cfg->active_threads_bitmask,
543-
task_bitmask_before_register);
544-
545-
CTEST_LOG_INFO("active_threads_bitmask=0x%lx\n",
546-
thread_cfg->active_threads_bitmask);
547-
548495
// This is the important call to initialize thread-specific stuff in
549496
// Splinter's task-system, which sets up the thread-id (index) and records
550497
// this thread as active with the task system.
551498
task_register_this_thread(thread_cfg->tasks, trunk_get_scratch_size());
552499

553-
uint64 task_bitmask_after_register =
554-
task_active_tasks_mask(thread_cfg->tasks);
555-
556-
// _Now, the active tasks bitmask should have changed.
557-
ASSERT_NOT_EQUAL(task_bitmask_before_register, task_bitmask_after_register);
558-
559500
threadid this_threads_idx = platform_get_tid();
560501
ASSERT_EQUAL(thread_cfg->exp_thread_idx,
561502
this_threads_idx,
562503
"exp_thread_idx=%lu, this_threads_idx=%lu\n",
563504
thread_cfg->exp_thread_idx,
564505
this_threads_idx);
565506

566-
// This thread is recorded as 'being active' by clearing its bit from the
567-
// mask.
568-
uint64 exp_bitmask = (0x1L << this_threads_idx);
569-
exp_bitmask = (task_bitmask_before_register & ~exp_bitmask);
570-
571-
CTEST_LOG_INFO("this_threads_idx=%lu"
572-
", task_bitmask_before_register=0x%lx"
573-
", task_bitmask_after_register=0x%lx"
574-
", exp_bitmask=0x%lx\n",
575-
this_threads_idx,
576-
task_bitmask_before_register,
577-
task_bitmask_after_register,
578-
exp_bitmask);
579-
580-
ASSERT_EQUAL(task_bitmask_after_register, exp_bitmask);
581-
582507
// Registration should have allocated some scratch space memory.
583508
ASSERT_TRUE(
584509
task_system_get_thread_scratch(thread_cfg->tasks, platform_get_tid())
@@ -591,12 +516,6 @@ exec_one_thread_use_lower_apis(void *arg)
591516

592517
task_deregister_this_thread(thread_cfg->tasks);
593518

594-
uint64 task_bitmask_after_deregister =
595-
task_active_tasks_mask(thread_cfg->tasks);
596-
597-
// De-registering this task removes it from the active tasks mask
598-
ASSERT_EQUAL(task_bitmask_before_register, task_bitmask_after_deregister);
599-
600519
// Deregistration releases scratch space memory.
601520
ASSERT_TRUE(
602521
task_system_get_thread_scratch(thread_cfg->tasks, this_threads_idx)
@@ -628,25 +547,9 @@ exec_one_thread_use_extern_apis(void *arg)
628547
{
629548
thread_config *thread_cfg = (thread_config *)arg;
630549

631-
uint64 bitmask_before_thread_create = thread_cfg->active_threads_bitmask;
632-
633-
uint64 bitmask_after_thread_create =
634-
task_active_tasks_mask(thread_cfg->tasks);
635-
636-
// The task_thread_create() -> task_invoke_with_hooks() also registers this
637-
// thread with Splinter. First, confirm that the bitmask has changed from
638-
// what it was before this thread was invoked.
639-
ASSERT_NOT_EQUAL(bitmask_before_thread_create, bitmask_after_thread_create);
640-
641550
threadid this_threads_idx = platform_get_tid();
642551
ASSERT_EQUAL(thread_cfg->exp_thread_idx, this_threads_idx);
643552

644-
// This thread is recorded as 'being active' by clearing its bit from the
645-
// mask. Verify the expected task bitmask.
646-
uint64 exp_bitmask = (0x1L << this_threads_idx);
647-
exp_bitmask = (bitmask_before_thread_create & ~exp_bitmask);
648-
ASSERT_EQUAL(bitmask_after_thread_create, exp_bitmask);
649-
650553
/*
651554
* Dead Code Warning!
652555
* The interface used here has already registered this thread. An attempt to

0 commit comments

Comments
 (0)