Bochs and QEMU concurrency test
Posted: Sat Aug 24, 2024 2:11 pm
Hi,
For my hobby OS, I'm currently using a single processor and have a preemptive kernel with time slicing. I disable interrupts to protect my scheduler queues and other state. No virtual memory or user mode support. Mostly C++ (gnu cross compiler) with a bit of assembly. No compiler optimization turned on. Everything is a kernel thread and is running nicely.
I'm now ready to start work on exploring proper locks. So I wrote a simple test to see the actual problem:
int count = 0;
void funP(void*)
{
for (int ii = 0; ii < 10000000; ii++) {
count++;
}
}
void testConcurrency()
{
Task* task1 = new Task("1", &funP, (void*) 1); // Name, function, argument
Task* task2 = new Task("2", &funP, (void*) 2);
task1->join();
task2->join();
printf("Count = %d\n", count);
}
To my surprise, QEMU always printed out 20000000. No concurrency issues!? The increments are perfectly interleaved. Bochs did what I expected and returned a different result every time 15782628, 15090785, 14213397, etc. Exact same image run by each tool.
I added a printf in the loop to check that the tasks were in fact being interleaved
void funP(void* arg)
{
int num = (int) arg;
for (int ii = 0; ii < 10000000; ii++) {
count++;
if ((ii % 1000000) == 0) {
printf("%d", num);
}
}
}
The (cleaned up) output was
12 12 12 21 ...
So it's not the case that task1 was run to completion then task2 was run.
Any idea on why they are different? I'd like to make QEMU run more realistically, but I don't know what to look for in the doc.
Thanks.
For my hobby OS, I'm currently using a single processor and have a preemptive kernel with time slicing. I disable interrupts to protect my scheduler queues and other state. No virtual memory or user mode support. Mostly C++ (gnu cross compiler) with a bit of assembly. No compiler optimization turned on. Everything is a kernel thread and is running nicely.
I'm now ready to start work on exploring proper locks. So I wrote a simple test to see the actual problem:
int count = 0;
void funP(void*)
{
for (int ii = 0; ii < 10000000; ii++) {
count++;
}
}
void testConcurrency()
{
Task* task1 = new Task("1", &funP, (void*) 1); // Name, function, argument
Task* task2 = new Task("2", &funP, (void*) 2);
task1->join();
task2->join();
printf("Count = %d\n", count);
}
To my surprise, QEMU always printed out 20000000. No concurrency issues!? The increments are perfectly interleaved. Bochs did what I expected and returned a different result every time 15782628, 15090785, 14213397, etc. Exact same image run by each tool.
I added a printf in the loop to check that the tasks were in fact being interleaved
void funP(void* arg)
{
int num = (int) arg;
for (int ii = 0; ii < 10000000; ii++) {
count++;
if ((ii % 1000000) == 0) {
printf("%d", num);
}
}
}
The (cleaned up) output was
12 12 12 21 ...
So it's not the case that task1 was run to completion then task2 was run.
Any idea on why they are different? I'd like to make QEMU run more realistically, but I don't know what to look for in the doc.
Thanks.