Skip to content

Commit 449e8b4

Browse files
🐛 Refactor task_notify_when_deleting to support task_to_notify dying before target_task (purduesigbots#100)
#### Summary: - Increments `configNUM_THREAD_LOCAL_STORAGE_POINTERS` again - Adds another linked list to thread local storage that will keep track of the tasks to whose death events `task_to_notify` is subscribed - When a task dies, the `task_delete_hook` now iterates through the task's list of subscriptions and removes references to itself from the subscriptions' lists of subscribers #### Motivation: Previously, the case in which `task_to_notify` dies before `target_task` was undefined behavior, because `target_task` would attempt to notify `task_to_notify` even if it's already been cleaned up. The naive solution of checking the `task_to_notify`'s state directly would not have worked because in certain cases (as with the competition tasks), the TCB can be reused. More information regarding the motivation behind this change can be found at purduesigbots#86, OkapiLib/OkapiLib#249, and OkapiLib/OkapiLib#321 ##### References (optional): Closes purduesigbots#86 #### Test Plan: - [x] execute test plan in src/tests/task_notify_when_deleting.c (regression test) (the following are adapted from purduesigbots#100 (comment). thanks @Octogonapus) - [x] delete `task_to_notify` and then delete `target_task`, verify nothing goes horrendously wrong - [x] repeat previous test with OkapiLib where an internal task is the `task_to_notify` #### Commits: * begin task_notify_when_deleting refactor * add nullcheck needed to check whether subscriptions_list is null in unsubscribe_hook_cb to handle the "normal operation" case, where target_task dies before task_to_notify * delete subscriptions list anyway * look for correct task in subscriptions_ll * mutex guard task_notify_when_deleting functions, cleanup includes * stick with one method of initializing mutex * Revert "stick with one method of initializing mutex" This reverts commit 154b969. * stick with the other method of initializing mutex
1 parent ad0f87c commit 449e8b4

File tree

4 files changed

+73
-11
lines changed

4 files changed

+73
-11
lines changed

include/pros/apix.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@ bool task_abort_delay(task_t task);
6060
* task_notify_ext(task_to_notify, value, action, NULL) when target_task is
6161
* deleted.
6262
*
63-
* NOTE: This facility does not support the case when task_to_notify
64-
* dies before target_task
6563
*
6664
* \param target_task
6765
* The task being watched for deletion

include/rtos/FreeRTOSConfig.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@
141141
#define configUSE_NEWLIB_REENTRANT 1
142142
#define configSTACK_DEPTH_TYPE size_t
143143

144-
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 1
144+
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 2
145145

146146
/* Include the query-heap CLI command to query the free heap space. */
147147
#define configINCLUDE_QUERY_HEAP_COMMAND 1

src/rtos/task_notify_when_deleting.c

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1+
#include "kapi.h"
12
#include "common/linkedlist.h"
2-
#include "rtos/task.h"
3+
4+
// NOTE: can't just include task.h because of redefinition that goes on in kapi
5+
// include chain, so we just prototype what we need here
6+
void *pvTaskGetThreadLocalStoragePointer( task_t xTaskToQuery, int32_t xIndex );
7+
void vTaskSetThreadLocalStoragePointer( task_t xTaskToSet, int32_t xIndex, void *pvValue );
8+
39
#include "rtos/tcb.h"
410

5-
// This increments configNUM_THREAD_LOCAL_STORAGE_POINTERS by 1
11+
// This increments configNUM_THREAD_LOCAL_STORAGE_POINTERS by 2
12+
13+
#define SUBSCRIBERS_TLSP_IDX 0
14+
#define SUBSCRIPTIONS_TLSP_IDX 1
615

7-
#define TLSP_IDX 0
16+
static static_sem_s_t task_notify_when_deleting_mutex_buf;
17+
static mutex_t task_notify_when_deleting_mutex;
818

919
struct notify_delete_action {
1020
task_t task_to_notify;
@@ -36,20 +46,51 @@ static struct notify_delete_action* _find_task(linked_list_s_t* ll, task_t task)
3646
return args.found_action;
3747
}
3848

49+
void task_notify_when_deleting_init() {
50+
task_notify_when_deleting_mutex = mutex_create_static(&task_notify_when_deleting_mutex_buf);
51+
}
52+
3953
void task_notify_when_deleting(task_t target_task, task_t task_to_notify,
4054
uint32_t value, notify_action_e_t notify_action) {
4155
task_to_notify = (task_to_notify == NULL) ? pxCurrentTCB : task_to_notify;
4256
target_task = (target_task == NULL) ? pxCurrentTCB : target_task;
4357

44-
// It doesn't make sense for a task to notify itself, and make sure that neither task is NULL (implying that scheduler hasn't started yet)
58+
// It doesn't make sense for a task to notify itself, and make sure that
59+
// neither task is NULL (implying that scheduler hasn't started yet)
4560
if (task_to_notify == target_task || !task_to_notify || !target_task) {
4661
return;
4762
}
4863

49-
linked_list_s_t* target_ll = pvTaskGetThreadLocalStoragePointer(target_task, TLSP_IDX);
64+
mutex_take(task_notify_when_deleting_mutex, TIMEOUT_MAX);
65+
66+
// task_to_notify maintains a list of the tasks whose deletion it cares about.
67+
// This will allow us to unsubscribe from notification if/when task_to_notify
68+
// is deleted
69+
linked_list_s_t* subscriptions_ll = pvTaskGetThreadLocalStoragePointer(task_to_notify, SUBSCRIPTIONS_TLSP_IDX);
70+
if (subscriptions_ll == NULL) {
71+
subscriptions_ll = linked_list_init();
72+
vTaskSetThreadLocalStoragePointer(task_to_notify, SUBSCRIPTIONS_TLSP_IDX, subscriptions_ll);
73+
}
74+
if (subscriptions_ll != NULL) {
75+
// check whether task_to_notify is already subscribed to target_task. if so,
76+
// do nothing
77+
ll_node_s_t* it = subscriptions_ll->head;
78+
bool found = false;
79+
while (it != NULL && !found) {
80+
found = it->payload.data == target_task;
81+
it = it->next;
82+
}
83+
if (!found) {
84+
linked_list_prepend_data(subscriptions_ll, target_task);
85+
}
86+
}
87+
88+
// similarly, target_task maintains a list of the tasks it needs to notify
89+
// when being deleted
90+
linked_list_s_t* target_ll = pvTaskGetThreadLocalStoragePointer(target_task, SUBSCRIBERS_TLSP_IDX);
5091
if (target_ll == NULL) {
5192
target_ll = linked_list_init();
52-
vTaskSetThreadLocalStoragePointer(target_task, TLSP_IDX, target_ll);
93+
vTaskSetThreadLocalStoragePointer(target_task, SUBSCRIBERS_TLSP_IDX, target_ll);
5394
}
5495

5596
if (target_ll != NULL) {
@@ -72,6 +113,7 @@ void task_notify_when_deleting(task_t target_task, task_t task_to_notify,
72113
action->value = value;
73114
}
74115
}
116+
mutex_give(task_notify_when_deleting_mutex);
75117
}
76118

77119
// NOTE: this code is untested, probably works, but also has a terrible name (task_notify_when_deleting_unsubscribe)
@@ -93,6 +135,15 @@ void task_notify_when_deleting(task_t target_task, task_t task_to_notify,
93135
// }
94136
// }
95137

138+
static void unsubscribe_hook_cb(ll_node_s_t* node, void* task_to_remove) {
139+
task_t subscription = node->payload.data;
140+
141+
linked_list_s_t* subscriptions_list = pvTaskGetThreadLocalStoragePointer(subscription, SUBSCRIBERS_TLSP_IDX);
142+
if (subscriptions_list != NULL) {
143+
linked_list_remove_data(subscriptions_list, task_to_remove);
144+
}
145+
}
146+
96147
static void delete_hook_cb(ll_node_s_t* node, void* ignore) {
97148
struct notify_delete_action* action = node->payload.data;
98149
if (action != NULL) {
@@ -103,10 +154,20 @@ static void delete_hook_cb(ll_node_s_t* node, void* ignore) {
103154
}
104155

105156
void task_notify_when_deleting_hook(task_t task) {
106-
linked_list_s_t* ll = pvTaskGetThreadLocalStoragePointer(task, TLSP_IDX);
157+
mutex_take(task_notify_when_deleting_mutex, TIMEOUT_MAX);
158+
// if this task was subscribed to any other task deletion events, unsubscribe
159+
linked_list_s_t* ll = pvTaskGetThreadLocalStoragePointer(task, SUBSCRIPTIONS_TLSP_IDX);
160+
if (ll != NULL) {
161+
linked_list_foreach(ll, unsubscribe_hook_cb, task);
162+
linked_list_free(ll);
163+
vTaskSetThreadLocalStoragePointer(task, SUBSCRIPTIONS_TLSP_IDX, NULL);
164+
}
165+
// notify subscribed tasks of this task's deletion
166+
ll = pvTaskGetThreadLocalStoragePointer(task, SUBSCRIBERS_TLSP_IDX);
107167
if (ll != NULL) {
108168
linked_list_foreach(ll, delete_hook_cb, NULL);
109169
linked_list_free(ll);
110-
vTaskSetThreadLocalStoragePointer(task, TLSP_IDX, NULL); // for good measure
170+
vTaskSetThreadLocalStoragePointer(task, SUBSCRIBERS_TLSP_IDX, NULL); // for good measure
111171
}
172+
mutex_give(task_notify_when_deleting_mutex);
112173
}

src/system/rtos_hooks.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ void rtos_initialize() {
4444
portDISABLE_INTERRUPTS();
4545

4646
vPortInstallFreeRTOSVectorTable();
47+
48+
void task_notify_when_deleting_init();
49+
task_notify_when_deleting_init();
4750
}
4851

4952
extern void FreeRTOS_Tick_Handler(void);

0 commit comments

Comments
 (0)