Set blackbox file handler to NULL after closing file
[inav.git] / src / main / scheduler / protothreads.h
blob66953d4612f8606566a4f0c5f8e602a3d0c7b233
1 /*
2 * This file is part of INAV.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
6 * You can obtain one at http://mozilla.org/MPL/2.0/.
8 * Alternatively, the contents of this file may be used under the terms
9 * of the GNU General Public License Version 3, as described below:
11 * This file is free software: you may copy, redistribute and/or modify
12 * it under the terms of the GNU General Public License as published by the
13 * Free Software Foundation, either version 3 of the License, or (at your
14 * option) any later version.
16 * This file is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
19 * Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see http://www.gnu.org/licenses/.
24 * Parts of this code are based on Protothreads (coroutines)
25 * written by Serge Zaitsev under MIT license
27 * Copyright (c) 2016 Serge Zaitsev
29 * Permission is hereby granted, free of charge, to any person obtaining a copy
30 * of this software and associated documentation files (the "Software"), to deal
31 * in the Software without restriction, including without limitation the rights
32 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33 * copies of the Software, and to permit persons to whom the Software is
34 * furnished to do so, subject to the following conditions:
36 * The above copyright notice and this permission notice shall be included in all
37 * copies or substantial portions of the Software.
40 #pragma once
41 #include "common/time.h"
42 #include "common/utils.h"
43 #include "drivers/time.h"
46 Protothreads are a extremely lightweight, stackless threads that provides a blocking context, without the overhead of per-thread stacks.
47 The purpose of protothreads is to implement sequential flow of control without using complex state machines or full multi-threading.
48 Protothreads provides conditional blocking inside a C function.
50 A protothread runs within a single C function and cannot span over other functions. A protothread may call normal C functions,
51 but cannot block inside a called function. Blocking inside nested function calls is instead made by spawning a separate protothread
52 for each potentially blocking function. The advantage of this approach is that blocking is explicit: the programmer knows exactly which
53 functions that may block that which functions that are not able block.
55 A protothread is driven by repeated calls to the function in which the protothread is running. Each time the function is called, the
56 protothread will run until it blocks or exits. Thus the scheduling of protothreads is done by the application that uses protothreads.
58 WARNING:
60 Because protothreads do not save the stack context across a blocking call, local variables are not preserved when the protothread blocks.
61 This means that local variables should be used with extreme caution - ultimately protothread function shouldn't use local variables at all.
64 struct ptState_s {
65 int line;
66 int returnCode;
67 timeUs_t startTime; // Used for non-blocking delays
68 timeDelta_t delayTime; // Used for non-blocking delays
71 // Define PT with given name
72 #define PROTOTHREAD(name) \
73 static struct ptState_s name##_protothreadState = {0, 0, 0, 0}; \
74 void name(void) \
76 #define STATIC_PROTOTHREAD(name) \
77 static struct ptState_s name##_protothreadState = {0, 0, 0, 0}; \
78 static void name(void) \
80 // Must be the first line in each protothread
81 #define ptBegin(name) \
82 struct ptState_s * currentPt = ptGetHandle(name); \
83 bool ptYielded = true; \
84 (void) ptYielded; \
85 switch (currentPt->line) { \
86 case 0:
88 // Generate pt pointer
89 #define ptGetHandle(name) (&name##_protothreadState)
91 // Restart protothread
92 #define ptRestart(handle) \
93 do { \
94 (handle)->returnCode = 0; \
95 (handle)->line = 0; \
96 } while(0)
98 // Returns true if protothread is stopped
99 #define ptIsStopped(handle) ((handle)->line < 0)
101 // Returns special value of protothread state (return value)
102 #define ptGetReturnCode(handle) ((handle)->returnCode)
104 // Low-level API to create continuation, normally should not be used
105 #define ptLabel() \
106 do { \
107 currentPt->line = __LINE__; \
108 FALLTHROUGH; \
109 case __LINE__:; \
110 } while (0)
112 // Suspends protothread until cond becomes true
113 // Condition is evaluated each call and shouldn't use any local variables
114 #define ptWait(condition) \
115 do { \
116 ptLabel(); \
117 if (!(condition)) { \
118 return; \
120 } while (0)
122 #define ptWaitTimeout(condition, timeoutMs) \
123 do { \
124 currentPt->startTime = (timeUs_t)millis(); \
125 currentPt->delayTime = (timeoutMs); \
126 ptLabel(); \
127 if (!(condition) && ((timeDelta_t)(millis() - (currentPt)->startTime) <= (currentPt)->delayTime)) { \
128 return; \
130 } while (0)
132 // Wait until thread finishes
133 #define ptWaitThread(name) \
134 do { \
135 ptLabel(); \
136 name(); \
137 if (!ptIsStopped(ptGetHandle(name))) { \
138 return; \
140 } while (0)
142 // Execute a protothread and wait for completion
143 #define ptSpawn(name) \
144 do { \
145 ptRestart(ptGetHandle(name)); \
146 ptWaitThread(name); \
147 } while (0)
149 // Suspends protothread for a given amount of time
150 // Delay is evaluated only once
151 #define ptDelayMs(delay) \
152 do { \
153 (currentPt)->startTime = (timeUs_t)millis(); \
154 (currentPt)->delayTime = (delay); \
155 ptLabel(); \
156 if ((timeDelta_t)(millis() - (currentPt)->startTime) <= (currentPt)->delayTime) { \
157 return; \
159 } while (0)
161 // Suspends protothread for a given amount of time
162 // Delay is evaluated only once
163 #define ptDelayUs(delay) \
164 do { \
165 (currentPt)->startTime = (timeUs_t)micros(); \
166 (currentPt)->delayTime = (delay); \
167 ptLabel(); \
168 if ((timeDelta_t)(micros() - (currentPt)->startTime) <= (currentPt)->delayTime) { \
169 return; \
171 } while (0)
173 // Suspends protothread until it's called again
174 #define ptYield() \
175 do { \
176 ptYielded = false; \
177 ptLabel(); \
178 if (!ptYielded) { \
179 return; \
181 } while (0)
183 // terminates current protothread pt with the given status. Protothread won't continue
184 #define ptStop(retCode) \
185 do { \
186 currentPt->returnCode = retCode; \
187 currentPt->line = (-1); \
188 return; \
189 } while (0)
191 // must be the last line in each protothread
192 #define ptEnd(retCode) \
193 ptStop(retCode); \
194 FALLTHROUGH; \
195 default: \
196 return; \
199 //***************************
200 // Abstract semaphore code
201 typedef bool ptSemaphore_t;
203 #define ptSemaphoreInit(sem) do { sem = false; } while (0)
204 #define ptSemaphoreWait(sem) do { ptWait(sem); sem = false; } while (0)
205 #define ptSemaphoreSignal(sem) do { sem = true; } while (0)