Concurrency considerations in generated code #541
Replies: 12 comments
-
@robdockins Frank is going to take a look at this. It may not make it into the very next release (3.6, to be frozen in 1 week), but I'm guessing we'll have dealt with this for good by the one release after the next one (3.7). |
Beta Was this translation helpful? Give feedback.
-
@fdedden Frank, were you ever able to look into this? I wonder if the compiler is adding any optimizations when volatile is not present. I mean, the variable is copied before it is used. Unless it inlines the copy such that it happens inside a call to the verification function that will actually use the value, I don't see what else could happen. It's not an unreasonable requirement of Copilot's API that all input variables should remain untouched until the call to It would be awesome to see an example of code where not having volatile makes a difference. |
Beta Was this translation helpful? Give feedback.
-
For my part, I'm more worried about the external interface code that links against the generated code, as opposed to the generated code itself. However, on low optimization in particular, I'd be surprised to see the compiler do anything untoward given that the globals are declared external, so perhaps the volatile keyword isn't necessary after all. I'd have to read the C standard pretty carefully to figure out if it makes a difference in this case. On the other hand, I think the additional stubs for mutual exclusion are pretty important for systems where it matters. |
Beta Was this translation helpful? Give feedback.
-
I've been thinking about this a bit more. It is clear to me that, if the compiler interleaves checks of monitors with copying inputs, then the meaning of the expressions monitored could change (handlers could change them). So I agree that addressing this might be a good idea, even if we have yet to find an actual example that breaks this. I'm not sure if having Copilot call functions like spec = do
trigger "step_end_critical_section" true []
<...other triggers...> That could call the function So maybe volatile is the only thing we "need" after all. To make an approach like the one in that code snippet not a hack, the order of execution of monitors should be properly documented (it should be mentioned in Copilot's operational semantics), the way to produce this effect should also be documented (tutorial?), and might even be abstracted away. |
Beta Was this translation helpful? Give feedback.
-
Agreed that the calling context could ensure that mutual exclusion is enabled before calling In particular, I would argue that an explicit A thought just occurred to me. Another option would be to split the |
Beta Was this translation helpful? Give feedback.
-
I agree. It may be better to have a dedicated construct for this.
I think this complicates things for users. Now the contract requires that they call two functions instead of one. At this point, my main thought is to try to keep the complexity as low as reasonably possible, both in Copilot's API, in the generated code, and in the implementation. Imagining that we execute a call to |
Beta Was this translation helpful? Give feedback.
-
(I said the above without even knowing if this would be generally understood by C99-compliant compilers.) |
Beta Was this translation helpful? Give feedback.
-
Ok, think of using the following: Main program ( void end_critical_section() __attribute__((weak));
int main()
{
if (end_critical_section) end_critical_section();
} Auxiliary module ( #include <stdio.h>
void end_critical_section()
{
printf("Hello world\n");
} If we only link $ gcc test.c -o main
$ ./main
$ If we link it all together, then it does: $ gcc test.c aux.c -o main
$ ./main
Hello world I just checked with GCC and it seems to accept this with Thoughts? How does it influence your verification efforts? EDIT: Cleaning; typos |
Beta Was this translation helpful? Give feedback.
-
That's an interesting idea, and I think it could work. I don't know that we have experimented with how weak symbols interact with our verification setup. I'll try it out and see what happens. |
Beta Was this translation helpful? Give feedback.
-
A little reading leads me to believe that weak symbols are not a truly portable feature, but are relatively well supported by GCC and clang. There do seem to be quite a few caveats and some differences in interpretation by system linkers. I'll leave it up to you if that is sufficient for your purposes. |
Beta Was this translation helpful? Give feedback.
-
We had a chat about this as well just now. One of the things I dislike about my proposal of using weak symbols is that the user gets no compile-time indication of whether their code is being picked up and will be called as intended. If they make a mistake (e.g., typo in the function name |
Beta Was this translation helpful? Give feedback.
-
Agreed, weak symbols are less fool-proof. |
Beta Was this translation helpful? Give feedback.
-
The generated C code takes some care to copy the values of all the "external" streams from shared global variables to private globals before beginning the remainder of its work. This is intended as a defense against concurrent or interrupt-handling code modifying the variables. According to the Copilot 3 manual:
This copying step is necessary, but not sufficient. At a minimum, the external global variables should be marked
volatile
, which indicates to the compiler that loads and stores involving these variables must not be hoisted, omitted or delayed. Probably these initial copies should also (optionally) be wrapped in a critical section to allow operating environments to mask interrupts or take a mutex while the copies are occurring.I suggest we add an option to
CSettings
, which, when set, will wrap this initial copying process in functions such asstep_start_critical_section()
andstep_end_critical_section()
which are intended to be implemented by the environment to take whatever steps might necessary to ensure reliable operation.Beta Was this translation helpful? Give feedback.
All reactions