You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/draft/timeout.md
+44-30Lines changed: 44 additions & 30 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,26 +2,35 @@
2
2
3
3
## 1. Configuration at Init Time
4
4
5
-
When creating a client, you provide a `whTimeoutConfig` specifying the timeout duration and an optional callback:
5
+
The timeout feature uses a callback-based abstraction (similar to the lock feature) that allows platform-specific timer implementations without introducing OS dependencies in core wolfHSM code. A platform port provides a callback table implementing the timer operations, and the core timeout module delegates to these callbacks.
6
+
7
+
When creating a client, you provide a `whTimeoutConfig` specifying the platform callbacks, platform context, and an optional application-level expired callback:
During `wh_Client_Init` (`src/wh_client.c:84-89`), the config is copied into an embedded `whTimeoutCtx respTimeout[1]` inside the client context via `wh_Timeout_Init()`. This stores the timeout duration and callback but doesn't start any timer yet.
20
-
If `respTimeoutConfig` is NULL, the timeout context is left zeroed and effectively disabled (a `timeoutUs` of 0 means "never expires").
28
+
During `wh_Client_Init`, the config is used to initialize an embedded `whTimeout respTimeout` inside the client context via `wh_Timeout_Init()`. This calls the platform `init` callback to set up timer resources but doesn't start any timer yet.
29
+
If `respTimeoutConfig` is NULL (or `cb` is NULL), the timeout is disabled and all operations become no-ops (timeout never expires).
21
30
22
31
## 2. What Happens During a Crypto Call
23
32
24
-
Before this PR, every crypto function in `wh_client_crypto.c` had this pattern after sending a request:
33
+
Before the timeout feature, every crypto function in `wh_client_crypto.c` had this pattern after sending a request:
25
34
```c
26
35
/* Old pattern -- infinite busy-wait */
27
36
do {
@@ -30,16 +39,16 @@ do {
30
39
```
31
40
32
41
If the server never responded, the client would spin forever.
33
-
The PR replaces all ~30 of these with a single helper `_recvCryptoResponse()` (`src/wh_client_crypto.c:165-180`):
42
+
This is replaced with a single helper `_recvCryptoResponse()` (`src/wh_client_crypto.c`):
ret = wh_Client_RecvResponseTimeout(ctx, group, action, size, data,
42
-
ctx->respTimeout);
50
+
ret = wh_Client_RecvResponseBlockingWithTimeout(ctx, group, action,
51
+
size, data);
43
52
#else
44
53
do {
45
54
ret = wh_Client_RecvResponse(ctx, group, action, size, data);
@@ -49,31 +58,30 @@ static int _recvCryptoResponse(whClientContext* ctx,
49
58
}
50
59
```
51
60
52
-
When timeout is enabled, it delegates to `wh_Client_RecvResponseTimeout`. When disabled, the old infinite-loop behavior is preserved.
61
+
When timeout is enabled, it delegates to `wh_Client_RecvResponseBlockingWithTimeout`. When disabled, the old infinite-loop behavior is preserved.
53
62
54
63
## 3. The Timeout Receive Loop
55
-
`wh_Client_RecvResponseTimeout` (`src/wh_client.c:211-231`) does this:
56
-
1. **Starts the timer** -- calls `wh_Timeout_Start()` which snapshots the current time via `WH_GETTIME_US()` into `timeout->startUs`.
64
+
`wh_Client_RecvResponseBlockingWithTimeout` (`src/wh_client.c`) does this:
65
+
1. **Starts the timer** -- calls `wh_Timeout_Start()` which delegates to the platform `start` callback (e.g. captures the current time).
57
66
2. **Polls for a response** -- calls `wh_Client_RecvResponse()` in a loop.
58
67
3. **On each `WH_ERROR_NOTREADY`**, checks `wh_Timeout_Expired()`:
59
-
- Gets the current time via `WH_GETTIME_US()`
60
-
- Computes `(now - startUs) >= timeoutUs`
61
-
- If expired: invokes the `expiredCb` (if set), then returns `WH_ERROR_TIMEOUT`
68
+
- Delegates to the platform `expired` callback to check elapsed time
69
+
- If expired: invokes the application `expiredCb` (if set), then returns `WH_ERROR_TIMEOUT`
62
70
- If not expired: loops again
63
71
4. **On any other return value** (success or error), returns immediately.
64
72
```
65
-
Client App _recvCryptoResponse wh_Timeout
73
+
Client App _recvCryptoResponse whTimeout
66
74
| | |
67
75
|-- wh_Client_AesCbc() --------> | |
68
-
| |-- wh_Timeout_Start --------> capture time
76
+
| |-- wh_Timeout_Start --------> cb->start()
69
77
| | |
70
78
| |-- RecvResponse (NOTREADY) |
71
-
| |-- Expired? ------------------> no
79
+
| |-- Expired? -------> cb->expired() -> no
72
80
| |-- RecvResponse (NOTREADY) |
73
-
| |-- Expired? ------------------> no
81
+
| |-- Expired? -------> cb->expired() -> no
74
82
| | ... |
75
83
| |-- RecvResponse (NOTREADY) |
76
-
| |-- Expired? ------------------> YES
84
+
| |-- Expired? -------> cb->expired() -> YES
77
85
| | |-- expiredCb()
78
86
|<-- WH_ERROR_TIMEOUT -----------| |
79
87
```
@@ -84,14 +92,14 @@ The `expiredCb` fires *before* the error is returned, so you can use it for logg
84
92
85
93
## 5. Overriding Expiration via the Callback
86
94
87
-
The expired callback receives a pointer to the `isExpired` flag and can override it by setting `*isExpired = 0`. This suppresses the expiration for the current check, allowing the polling loop to continue. A common use case is to extend the timeout deadline: clear the flag, then call `wh_Timeout_Start()` to restart the timer.
95
+
The application expired callback receives a pointer to the `isExpired` flag and can override it by setting `*isExpired = 0`. This suppresses the expiration for the current check, allowing the polling loop to continue. A common use case is to extend the timeout deadline: clear the flag, then call `wh_Timeout_Start()` to restart the timer.
88
96
89
97
The callback can also return a non-zero error code to signal a failure. When it does, `wh_Timeout_Expired()` propagates that error directly to the caller instead of returning the expired flag.
90
98
91
99
```c
92
-
static int myOverrideCb(whTimeoutCtx* ctx, int* isExpired)
100
+
static int myOverrideCb(whTimeout* timeout, int* isExpired)
93
101
{
94
-
int* retryCount = (int*)ctx->cbCtx;
102
+
int* retryCount = (int*)timeout->expiredCtx;
95
103
if (retryCount == NULL) {
96
104
return WH_ERROR_BADARGS;
97
105
}
@@ -101,21 +109,27 @@ static int myOverrideCb(whTimeoutCtx* ctx, int* isExpired)
101
109
if (*retryCount <= 1) {
102
110
/* First expiration: suppress and restart the timer */
103
111
*isExpired = 0;
104
-
wh_Timeout_Start(ctx);
112
+
wh_Timeout_Start(timeout);
105
113
}
106
114
/* Subsequent expirations: allow the timeout to fire */
-**Only crypto responses are covered.** Non-crypto client calls (key management, NVM operations, comm init) still use the old infinite-wait pattern. The timeout is specifically wired into `_recvCryptoResponse`.
121
-
-**The timeout is per-client, not per-call.** All crypto operations for a given client share the same `respTimeout` context with the same duration. You can call `wh_Timeout_Set(ctx->respTimeout, newValue)` to change it between calls, but there's no per-operation override.
135
+
-**The timeout is per-client, not per-call.** All crypto operations for a given client share the same `respTimeout` context with the same duration. You can call `wh_Timeout_Set()` to change the duration between calls, but there's no per-operation override.
0 commit comments