Branch data Line data Source code
1 : : #include "rational.h"
2 : : #include <stdio.h>
3 : :
4 : : /**
5 : : * @brief Shared process status used when normalizing function return values
6 : : *
7 : : * @details Starts at SUCCESS. Code outside the current function, such as a
8 : : * signal handler, can set process-wide status here. Only INFO,
9 : : * WARNING, and HALTED propagate into function returns through GLOBAL
10 : : */
11 : : _Atomic Return global_return_status = SUCCESS;
12 : :
13 : : /**
14 : : * @brief Normalize one function Return value with global context
15 : : *
16 : : * @details The function normalizes @p status, normalizes global_return_status,
17 : : * stores the normalized global status back, merges only GLOBAL bits
18 : : * from global_return_status into @p status, and normalizes the final
19 : : * result. Technical critical bits take precedence over SUCCESS.
20 : : * NO takes precedence over YES when both binary answer flags are present
21 : : *
22 : : * @param status Status flags to normalize
23 : : * @return Normalized status flags
24 : : */
25 : 3638765 : Return rational_normalize_return(Return status)
26 : : {
27 : : /* CRITICAL marks an internal or blocking problem.
28 : : Such a status cannot also be SUCCESS, even if SUCCESS was ORed in earlier */
29 [ + + ]: 3638765 : if(CRITICAL & status)
30 : : {
31 : 2182 : status &= ~SUCCESS;
32 : : }
33 : :
34 : : /* NO is the dominant binary answer.
35 : : If a check answered no, the final binary answer cannot also be yes.
36 : : If both binary answers are present, keep NO and drop YES */
37 [ + + ]: 3638765 : if(NO & status)
38 : : {
39 : 10835 : status &= ~YES;
40 : : }
41 : :
42 : : /* Local status is not the only source of process state.
43 : : global_return_status carries context that may be set outside the current function.
44 : : Read it into a regular Return value, normalize that copy, store the cleaned copy back,
45 : : then merge only explicitly global bits into the function return */
46 : 3638765 : Return global_status = atomic_load(&global_return_status);
47 : :
48 : : /* Global status follows the same contradiction rules as local status.
49 : : The normalized value is stored back so future returns see cleaned flags */
50 [ + + ]: 3638765 : if(CRITICAL & global_status)
51 : : {
52 : 1 : global_status &= ~SUCCESS;
53 : : }
54 : :
55 [ + + ]: 3638765 : if(NO & global_status)
56 : : {
57 : 2 : global_status &= ~YES;
58 : : }
59 : :
60 : : /* Keep the process-wide status normalized for the next function return */
61 : 3638765 : atomic_store(&global_return_status,global_status);
62 : :
63 : : /* Only process-wide context bits may flow from global status to a return.
64 : : Binary answers and local-only flags must not leak between functions */
65 : 3638765 : status |= (global_status & GLOBAL);
66 : :
67 : : /* Merging global context can introduce contradictions.
68 : : Normalize once more before returning the final function status */
69 [ + + ]: 3638765 : if(CRITICAL & status)
70 : : {
71 : 2182 : status &= ~SUCCESS;
72 : : }
73 : :
74 [ + + ]: 3638765 : if(NO & status)
75 : : {
76 : 10835 : status &= ~YES;
77 : : }
78 : :
79 : 3638765 : return(status);
80 : : }
81 : :
82 : : /**
83 : : * @brief Consume a yes/no Return answer and merge its technical status
84 : : *
85 : : * @details The returned status must contain a pending yes/no answer. The
86 : : * function reads YES or NO, removes AWAITING and DECISION from the
87 : : * caller's local status, merges only the technical status bits, and
88 : : * returns a regular C bool for direct use in conditions
89 : : *
90 : : * @param status Local caller status that accumulates technical return flags
91 : : * @param returned Return value produced by a check function
92 : : * @param func Name of the caller function for diagnostics
93 : : * @param line Source line of the ask() call for diagnostics
94 : : * @return true when @p returned contains a non-critical YES answer
95 : : */
96 : 5791 : bool rational_ask(
97 : : Return *status,
98 : : Return returned,
99 : : const char *func,
100 : : const int line)
101 : : {
102 : 5791 : bool answer = false;
103 : :
104 [ + + ]: 5791 : if(status == NULL)
105 : : {
106 : 1 : slog(ERROR,"%s:%d ask() received NULL status storage\n",func,line);
107 : :
108 : 1 : return(false);
109 : : }
110 : :
111 : 5790 : returned = rational_normalize_return(returned);
112 : :
113 [ + + ]: 5790 : if((AWAITING & returned) == 0)
114 : : {
115 : 1 : slog(ERROR,"%s:%d ask() expected a yes/no Return answer\n",func,line);
116 : 1 : *status = rational_normalize_return((*status & ~(AWAITING | DECISION)) | FAILURE);
117 : :
118 : 1 : return(false);
119 : : }
120 : :
121 [ + + ]: 5789 : if((DECISION & returned) == 0)
122 : : {
123 : 1 : slog(ERROR,"%s:%d ask() received a pending Return answer without YES or NO\n",func,line);
124 : 1 : *status = rational_normalize_return((*status & ~(AWAITING | DECISION)) | FAILURE);
125 : :
126 : 1 : return(false);
127 : : }
128 : :
129 [ + + + + ]: 5788 : if((CRITICAL & returned) == 0 && (YES & returned))
130 : : {
131 : 371 : answer = true;
132 : : }
133 : :
134 : 11576 : *status = rational_normalize_return(
135 : 5788 : (*status & ~(AWAITING | DECISION))
136 : 5788 : | (returned & ~(AWAITING | DECISION)));
137 : :
138 : 5788 : return(answer);
139 : : }
140 : :
141 : : /// Converts Return status flags to a string
142 : 1725 : const char *show_status(const Return status)
143 : : {
144 : 1725 : const char *status_label = "UNKNOWN";
145 : :
146 : : /* Keep behavior explicit for the zero status value. */
147 [ + + ]: 1725 : if(status == OK)
148 : : {
149 : 1 : status_label = "OK";
150 : : } else {
151 : : static char buffer[MAX_CHARACTERS];
152 : 1724 : buffer[0] = '\0';
153 : :
154 : : static const struct {
155 : : Return flag;
156 : : const char *name;
157 : : } mapping[] = {
158 : : {FAILURE,"FAILURE"},
159 : : {SUCCESS,"SUCCESS"},
160 : : {HALTED,"HALTED"},
161 : : {WARNING,"WARNING"},
162 : : {DONOTHING,"DONOTHING"},
163 : : {INFO,"INFO"},
164 : : {YES,"YES"},
165 : : {NO,"NO"},
166 : : {AWAITING,"AWAITING"},
167 : : {0,NULL}
168 : : };
169 : :
170 : 1724 : size_t used = 0U;
171 : 1724 : bool first = true;
172 : 1724 : unsigned int remaining = (unsigned int)status;
173 : :
174 [ + + ]: 17222 : for(size_t i = 0U; mapping[i].name != NULL; i++)
175 : : {
176 : 15500 : const unsigned int flag = (unsigned int)mapping[i].flag;
177 : :
178 [ + + ]: 15500 : if((remaining & flag) == 0U)
179 : : {
180 : 13489 : continue;
181 : : }
182 : :
183 : 2011 : const int written = snprintf(
184 : : buffer + used,
185 : : MAX_CHARACTERS - used,
186 : : "%s%s",
187 : : first ? "" : "|",
188 [ + + ]: 2011 : mapping[i].name);
189 : :
190 [ + + ]: 2011 : if(written < 0)
191 : : {
192 : 1 : break;
193 : : }
194 : :
195 [ + + ]: 2010 : if((size_t)written >= (MAX_CHARACTERS - used))
196 : : {
197 : 1 : buffer[MAX_CHARACTERS - 1U] = '\0';
198 : 1 : status_label = buffer;
199 : 1 : first = false;
200 : 1 : break;
201 : : }
202 : :
203 : 2009 : used += (size_t)written;
204 : 2009 : first = false;
205 : 2009 : remaining &= ~flag;
206 : : }
207 : :
208 [ + + ]: 1724 : if(first == false)
209 : : {
210 : 1722 : status_label = buffer;
211 : : }
212 : : }
213 : 1725 : return(status_label);
214 : : }
|