freebsd_network: Rewrite ConditionVariable-using code.

The old code was not at all correct with respect to interlocking.
The new code should be, and is also much simpler.
This commit is contained in:
Augustin Cavalier 2022-02-03 18:09:41 -05:00
parent 1960df3158
commit 33c03035d1
8 changed files with 95 additions and 218 deletions

View File

@ -1,93 +0,0 @@
/*
* Copyright 2009 Colin Günther, coling@gmx.de
* All Rights Reserved. Distributed under the terms of the MIT License.
*/
extern "C" {
#include <compat/sys/condvar.h>
#include <compat/sys/kernel.h>
}
#include "Condvar.h"
void
conditionInit(struct cv* variable, const char* description)
{
variable->condition.Init(variable, description);
}
void
conditionPublish(struct cv* variable, const void* waitChannel,
const char* description)
{
variable->condition.Publish(waitChannel, description);
}
void
conditionUnpublish(struct cv* variable)
{
variable->condition.Unpublish();
}
int
conditionTimedWait(struct cv* variable, const int timeout)
{
status_t status = variable->condition.Wait(B_RELATIVE_TIMEOUT,
TICKS_2_USEC(timeout));
if (status != B_OK)
status = EWOULDBLOCK;
return status;
}
void
conditionWait(struct cv* variable)
{
variable->condition.Wait();
}
void
conditionNotifyOne(struct cv* variable)
{
variable->condition.NotifyOne();
}
int
publishedConditionTimedWait(const void* waitChannel, const int timeout)
{
ConditionVariableEntry variableEntry;
bigtime_t usecs = TICKS_2_USEC(timeout);
// FreeBSD has a condition-variable scheduling system with different
// scheduling semantics than ours does. As a result, it seems there are
// some scenarios which work fine under FreeBSD but race into a deadlock
// on Haiku. To avoid this, turn unlimited timeouts into 1sec ones.
status_t status = variableEntry.Wait(waitChannel, B_RELATIVE_TIMEOUT,
usecs > 0 ? usecs : 1000 * 1000);
if (status != B_OK)
status = EWOULDBLOCK;
return status;
}
void
publishedConditionNotifyAll(const void* waitChannel)
{
ConditionVariable::NotifyAll(waitChannel, B_OK);
}
void
publishedConditionNotifyOne(const void* waitChannel)
{
ConditionVariable::NotifyOne(waitChannel, B_OK);
}

View File

@ -1,27 +0,0 @@
/*
* Copyright 2009 Colin Günther, coling@gmx.de
* All Rights Reserved. Distributed under the terms of the MIT License.
*/
#ifndef CONDVAR_H_
#define CONDVAR_H_
#ifdef __cplusplus
extern "C" {
#endif
void conditionInit(struct cv*, const char*);
void conditionPublish(struct cv*, const void*, const char*);
void conditionUnpublish(struct cv*);
int conditionTimedWait(struct cv*, const int);
void conditionWait(struct cv*);
void conditionNotifyOne(struct cv*);
int publishedConditionTimedWait(const void*, const int);
void publishedConditionNotifyAll(const void*);
void publishedConditionNotifyOne(const void*);
#ifdef __cplusplus
}
#endif
#endif /* CONDVAR_H_ */

View File

@ -18,8 +18,7 @@ KernelStaticLibrary libfreebsd_network.a :
bus_dma.cpp
callout.cpp
clock.c
condvar.c
Condvar.cpp
condvar.cpp
device.c
device_hooks.c
driver.c
@ -45,7 +44,7 @@ KernelStaticLibrary libfreebsd_network.a :
priv.cpp
smp.c
subr_autoconf.cpp
synch.c
synch.cpp
systm.cpp
sysinit.c
taskqueue.c

View File

@ -99,8 +99,6 @@ extern int vsnprintf(char *, size_t, const char *, __va_list)
int msleep(void *, struct mtx *, int, const char *, int);
int _pause(const char *, int);
#define pause(waitMessage, timeout) _pause((waitMessage), (timeout))
#define tsleep(channel, priority, waitMessage, timeout) \
msleep((channel), NULL, (priority), (waitMessage), (timeout))
#define msleep_spin(chan, mtx, wmesg, timo) \
msleep(chan, mtx, PZERO, wmesg, timo)
#define mtx_sleep msleep

View File

@ -1,42 +0,0 @@
/*
* Copyright 2009 Colin Günther, coling@gmx.de.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <compat/sys/mutex.h>
#include <compat/sys/condvar.h>
#include "Condvar.h"
void cv_init(struct cv* variable, const char* description)
{
conditionInit(variable, description);
}
void cv_signal(struct cv* variable)
{
conditionNotifyOne(variable);
}
int cv_timedwait(struct cv* variable, struct mtx* mutex, int timeout)
{
int status;
mtx_unlock(mutex);
status = conditionTimedWait(variable, timeout);
mtx_lock(mutex);
return status;
}
void cv_wait(struct cv* variable, struct mtx* mutex)
{
mtx_unlock(mutex);
conditionWait(variable);
mtx_lock(mutex);
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2022, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*/
#include <compat/sys/mutex.h>
#include <compat/sys/condvar.h>
#include <compat/sys/kernel.h>
void
cv_init(struct cv* variable, const char* description)
{
variable->condition.Init(NULL, description);
}
void
cv_signal(struct cv* variable)
{
variable->condition.NotifyOne();
}
int
cv_timedwait(struct cv* variable, struct mtx* mutex, int timeout)
{
int status;
const uint32 flags = timeout ? B_RELATIVE_TIMEOUT : 0;
const bigtime_t bigtimeout = TICKS_2_USEC(timeout);
if (mutex->type == MTX_RECURSE) {
// Special case: let the ConditionVariable handle switching recursive locks.
status = variable->condition.Wait(&mutex->u.recursive,
flags, bigtimeout);
return status;
}
ConditionVariableEntry entry;
variable->condition.Add(&entry);
mtx_unlock(mutex);
status = entry.Wait(flags, bigtimeout);
mtx_lock(mutex);
return status;
}
void
cv_wait(struct cv* variable, struct mtx* mutex)
{
cv_timedwait(variable, mutex, 0);
}

View File

@ -1,51 +0,0 @@
/*
* Copyright 2009 Colin Günther, coling@gmx.de
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <compat/sys/systm.h>
#include <compat/sys/kernel.h>
#include <compat/sys/mutex.h>
#include <compat/sys/condvar.h>
#include "Condvar.h"
int
msleep(void* identifier, struct mtx* mutex, int priority,
const char* description, int timeout)
{
int status;
struct cv sleep;
conditionPublish(&sleep, identifier, description);
// FreeBSD's msleep() does not allow the mutex to be NULL, but we
// do, as we implement some other functions like tsleep() with it.
if (mutex != NULL)
mtx_unlock(mutex);
status = publishedConditionTimedWait(identifier, timeout);
if (mutex != NULL)
mtx_lock(mutex);
conditionUnpublish(&sleep);
return status;
}
void
wakeup(void* identifier)
{
publishedConditionNotifyAll(identifier);
}
void
wakeup_one(void* identifier)
{
publishedConditionNotifyOne(identifier);
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2022, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*/
#include <compat/sys/systm.h>
#include <compat/sys/kernel.h>
#include <compat/sys/mutex.h>
#include <compat/sys/condvar.h>
int
msleep(void* identifier, struct mtx* mutex, int priority,
const char* description, int timeout)
{
struct cv channel;
channel.condition.Publish(identifier, description);
int status = cv_timedwait(&channel, mutex, timeout);
channel.condition.Unpublish();
return status;
}
void
wakeup(void* identifier)
{
ConditionVariable::NotifyAll(identifier, B_OK);
}
void
wakeup_one(void* identifier)
{
ConditionVariable::NotifyOne(identifier, B_OK);
}