-
Notifications
You must be signed in to change notification settings - Fork 1
/
TISM_Postman.c
228 lines (187 loc) · 11.3 KB
/
TISM_Postman.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/*
TISM_Postman.c
==============
Tools for managing the postboxes (outbound and inbound queues) and delivery of messages between tasks.
The TISM_Postman process:
- Each core runs an instance of TISM_Scheduler; each instance has its own outbound messaging queue (circular buffer).
This is used for outgoing messages, generated by tasks during their run cycle.
- Each task has a separate inbound message queue (circular buffer).
- When a task cycle is completed, TISM_Scheduler checks the outbound queue for messages to be processed. If these are
present, TISM_Postman is called by TISM_Scheduler to process messages for that specific outbound queue.
- TISM_Postman handles the delivery of messages by processing the messages from the outbound queue for the specific
scheduler-instance, and delivers it to the inbound queue of the specified task.
- As only one instance of TISM_Postman can run at a time, this process is thread-safe.
TISM_Postman uses the functions in TISM_Messaging; the definitions of messaging struct is defined in TISM_Definitions.
The outbound queues for the TISM_Scheduler instances are global variables; OutboundMessageQueue[CORE0] and OutboundMessageQueue[CORE1].
TISM_Postman provides for 4 consumer functions to easily manage messaging.
Copyright (c) 2024 Maarten Klarenbeek (https://github.com/mjklaren)
Distributed under the GPLv3 license
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "TISM.h"
/*
Description
Wrapper for TISM_CircularBufferMessagesWaiting; allows tasks to easer check if a message is waiting in their inbound queue.
Parameters:
TISM_Task ThisTask - Struct containing all task related information.
Return value:
<value> - Integer value of number of messages waiting
0 - No messages waiting
*/
uint16_t TISM_PostmanMessagesWaiting(TISM_Task ThisTask)
{
return(TISM_CircularBufferMessagesWaiting(ThisTask.InboundMessageQueue));
}
/*
Description
Wrapper for TISM_CircularBufferWrite; allows tasks to easier write messages to the outbound queue.
Parameters:
TISM_Task ThisTask - Struct containing all task related information.
uint8_t RecipientTaskID - TaskID of the recipient.
uint8_t MessageType - Type of message (see TISM_Definitions.h).
uint32_t Message - Message. Could also contain a pointer to something (e.g. text buffer).
uint32_t Specification - Specification to the provided message. Could also contain a pointer to something (e.g. text buffer).
Return value:
false - Buffer is full.
true - Succes
*/
bool TISM_PostmanWriteMessage(TISM_Task ThisTask, uint8_t RecipientTaskID, uint8_t MessageType, uint32_t Message, uint32_t Specification)
{
return(TISM_CircularBufferWriteWithTimestamp(ThisTask.OutboundMessageQueue, ThisTask.TaskID, RecipientTaskID, MessageType, Message, Specification, time_us_64()));
}
/*
Description
Wrapper for TISM_CircularBufferRead; allows tasks to easier read messages from the inbound queue.
Parameters:
TISM_Task ThisTask - Struct containing all task related information.
Return value:
*TISM_message - Pointer to message of type struct TISM_Message; the current message in the buffer.
*/
struct TISM_Message *TISM_PostmanReadMessage(TISM_Task ThisTask)
{
return(TISM_CircularBufferRead(ThisTask.InboundMessageQueue));
}
/*
Description
Wrapper for TISM_CircularBufferDelete; allows tasks to easier delete the first message from their inbound queue.
Parameters:
TISM_Task ThisTask - Struct containing all task related information.
Return value:
none
*/
void TISM_PostmanDeleteMessage(TISM_Task ThisTask)
{
TISM_CircularBufferDelete(ThisTask.InboundMessageQueue);
}
// The structure containing all data for TISM_Postman to run.
struct TISM_PostmanData
{
bool TaskReceivedMessage[MAX_TASKS];
} TISM_PostmanData;
/*
Description
The main task for the Postman of TISM. Handles the distribution of messages between tasks.
This function is called by TISM_Scheduler.
Parameters:
TISM_Task ThisTask - Struct containing all task related information.
Return value:
OK - Task run completed succesfully.
None-zero value - Error
*/
uint8_t TISM_Postman (TISM_Task ThisTask)
{
if (ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Run starting.");
switch(ThisTask.TaskState)
{
case INIT: // Task required to initialize
if (ThisTask.TaskDebug) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Initializing with priority %d.", ThisTask.TaskPriority);
// Empty the register we use to track which tasks we need to send a wake-up request for.
for(uint8_t counter=0;counter<MAX_TASKS;counter++)
TISM_PostmanData.TaskReceivedMessage[counter]=false;
break;
case RUN: // Do the work
if (ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Doing work with priority %d on core %d.", ThisTask.TaskPriority, ThisTask.RunningOnCoreID);
// We put a limit on message processed in each run, to prevent tasks claiming all of the system.
uint16_t MessageCounter=0;
TISM_Message *MessageToProcess;
// First check for pending messages.
while((TISM_PostmanMessagesWaiting(ThisTask)>0) && (MessageCounter<MAX_MESSAGES))
{
MessageToProcess=TISM_PostmanReadMessage(ThisTask);
if (ThisTask.TaskDebug) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Message '%ld' type %d from TaskID %d (%s) received.", MessageToProcess->Message, MessageToProcess->MessageType, MessageToProcess->SenderTaskID, System.Task[MessageToProcess->SenderTaskID].TaskName);
// Processed the message; delete it.
switch(MessageToProcess->MessageType)
{
case TISM_PING: // Check if this process is still alive. Reply with a ECHO message type; return same message payload.
TISM_PostmanWriteMessage(ThisTask,MessageToProcess->SenderTaskID,TISM_ECHO,MessageToProcess->Message,0);
break;
default: // Unknown message type - ignore.
break;
}
TISM_PostmanDeleteMessage(ThisTask);
MessageCounter++;
}
// Check the outbound queues for both cores for messages.
for(uint8_t CoreCounter=0;CoreCounter<MAX_CORES;CoreCounter++)
{
while((TISM_CircularBufferMessagesWaiting(&OutboundMessageQueue[CoreCounter])>0) && (MessageCounter<MAX_MESSAGES))
{
MessageToProcess=TISM_CircularBufferRead(&OutboundMessageQueue[CoreCounter]);
if (ThisTask.TaskDebug) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Processing message '%ld' from the queue of core %d type %d from TaskID %d (%s) to %d (%s).", MessageToProcess->Message, CoreCounter, MessageToProcess->MessageType, MessageToProcess->SenderTaskID, System.Task[MessageToProcess->SenderTaskID].TaskName, MessageToProcess->RecipientTaskID, System.Task[MessageToProcess->RecipientTaskID].TaskName);
// Write this message to the inbound queue of SenderTaskID. Check validity of the recipient ID.
if((MessageToProcess->RecipientTaskID>=0) &&
(MessageToProcess->RecipientTaskID<System.NumberOfTasks) &&
(!TISM_CircularBufferWriteWithTimestamp (&InboundMessageQueue[MessageToProcess->RecipientTaskID], MessageToProcess->SenderTaskID, MessageToProcess->RecipientTaskID, MessageToProcess->MessageType, MessageToProcess->Message, MessageToProcess->Specification, MessageToProcess->MessageTimestamp)))
{
// Failure in delivery - buffer full? Give warning.
// Don't use the system logger - doesn't make sense to use it when there are issues with circulair buffers.
fprintf(STDERR, "%llu %s (ID %d) ERROR: Message '%ld' type %d from TaskID %d to %d could not be delivered.", time_us_64(), ThisTask.TaskName, ThisTask.TaskID, MessageToProcess->Message, MessageToProcess->MessageType, MessageToProcess->SenderTaskID, MessageToProcess->RecipientTaskID);
// Prevent a memory leak; when a message is sent to the EventLogger, free the claimed memory.
if(MessageToProcess->RecipientTaskID==System.TISM_EventLoggerTaskID && (MessageToProcess->MessageType==TISM_LOG_EVENT_NOTIFY || MessageToProcess->MessageType==TISM_LOG_EVENT_ERROR))
{
// Messages from this type sent to EventHandler have claimed memory, Release it to prevent memory leaks.
fprintf(STDERR, " Attempting to free claimed memory.");
free((char *)MessageToProcess->Message);
}
fprintf(STDERR, "\n");
}
else
{
// Note that we need to ask TaskManager to wake the recipient.
// Further note, we do not have to ask TaskManager and IRQHandler to wake itself.
if(MessageToProcess->RecipientTaskID!=System.TISM_TaskManagerTaskID)
TISM_PostmanData.TaskReceivedMessage[MessageToProcess->RecipientTaskID]=true;
}
// Processed the message; delete it.
TISM_CircularBufferDelete(&OutboundMessageQueue[CoreCounter]);
MessageCounter++;
}
}
// Now send messages to TaskManager to wake all processes who have received a message.
for(uint8_t counter=0;counter<System.NumberOfTasks;counter++)
{
if(TISM_PostmanData.TaskReceivedMessage[counter])
{
TISM_CircularBufferWrite(&InboundMessageQueue[System.TISM_TaskManagerTaskID],ThisTask.TaskID,System.TISM_TaskManagerTaskID,TISM_SET_TASK_SLEEP,false,counter);
TISM_PostmanData.TaskReceivedMessage[counter]=false;
}
}
// Go to sleep; we only wake on incoming messages.
// We do it directly here to prevent circulair dependencies with TISM_TaskManager.
System.Task[System.TISM_PostmanTaskID].TaskSleeping=true;
// All done.
break;
case STOP: // Task required to stop
if (ThisTask.TaskDebug) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Stopping.");
// Tasks for stopping
// Set the task state to DOWN. We do it directly here to prevent circulair dependencies with TISM_TaskManager.
System.Task[System.TISM_PostmanTaskID].TaskState=DOWN;
break;
default: // All other states (e.g. SLEEP) are ignored/no action.
break;
}
// All done.
if (ThisTask.TaskDebug==DEBUG_HIGH) TISM_EventLoggerLogEvent (ThisTask, TISM_LOG_EVENT_NOTIFY, "Run completed.");
return (OK);
}