mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
[client] providers: handle fallback disconnects safely
This commit is contained in:
@@ -29,6 +29,8 @@ void lgInput_init(void);
|
||||
void lgInput_free(void);
|
||||
|
||||
void lgInput_setFallback(const LG_InputOps * ops, void * opaque);
|
||||
/* Drop functions remove a dead endpoint without invoking it. */
|
||||
void lgInput_dropFallback(void);
|
||||
void lgInput_setTransport(const LG_InputOps * ops, void * opaque);
|
||||
void lgInput_dropTransport(void);
|
||||
void lgInput_useTransport(bool enable);
|
||||
|
||||
@@ -4335,12 +4335,7 @@ void lgAudio_setFallback(const LG_AudioOps * ops, void * opaque)
|
||||
setBinding(&audio.fallback, ops, opaque, fallbackStatusChanged);
|
||||
}
|
||||
|
||||
void lgAudio_setTransport(const LG_AudioOps * ops, void * opaque)
|
||||
{
|
||||
setBinding(&audio.transport, ops, opaque, transportStatusChanged);
|
||||
}
|
||||
|
||||
void lgAudio_dropTransport(void)
|
||||
static void dropBinding(AudioBinding * target)
|
||||
{
|
||||
LG_LOCK(audio.bindingLock);
|
||||
if (!atomic_load_explicit(&audio.ready, memory_order_acquire))
|
||||
@@ -4351,16 +4346,27 @@ void lgAudio_dropTransport(void)
|
||||
|
||||
LG_LOCK(audio.providerLock);
|
||||
LG_LOCK_EXCLUSIVE(audio.activeLock);
|
||||
const AudioBinding old = audio.transport;
|
||||
const bool wasActive = bindingActiveNL(&audio.transport);
|
||||
audio.transport = (AudioBinding) { 0 };
|
||||
const bool wasActive = bindingActiveNL(target);
|
||||
*target = (AudioBinding) { 0 };
|
||||
LG_UNLOCK_EXCLUSIVE(audio.activeLock);
|
||||
updateActive(wasActive);
|
||||
LG_UNLOCK(audio.providerLock);
|
||||
|
||||
if (old.ops && old.ops->setStatusListener)
|
||||
old.ops->setStatusListener(old.opaque, NULL, NULL);
|
||||
LG_UNLOCK(audio.bindingLock);
|
||||
}
|
||||
|
||||
void lgAudio_dropFallback(void)
|
||||
{
|
||||
dropBinding(&audio.fallback);
|
||||
}
|
||||
|
||||
void lgAudio_setTransport(const LG_AudioOps * ops, void * opaque)
|
||||
{
|
||||
setBinding(&audio.transport, ops, opaque, transportStatusChanged);
|
||||
}
|
||||
|
||||
void lgAudio_dropTransport(void)
|
||||
{
|
||||
dropBinding(&audio.transport);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -29,6 +29,8 @@ void lgAudio_init(void);
|
||||
void lgAudio_free(void);
|
||||
|
||||
void lgAudio_setFallback(const LG_AudioOps * ops, void * opaque);
|
||||
/* Drop functions remove a dead endpoint without invoking it. */
|
||||
void lgAudio_dropFallback(void);
|
||||
void lgAudio_setTransport(const LG_AudioOps * ops, void * opaque);
|
||||
void lgAudio_dropTransport(void);
|
||||
|
||||
@@ -42,6 +44,7 @@ static inline void lgAudio_init(void) {}
|
||||
static inline void lgAudio_free(void) {}
|
||||
static inline void lgAudio_setFallback(
|
||||
const LG_AudioOps * ops, void * opaque) {}
|
||||
static inline void lgAudio_dropFallback(void) {}
|
||||
static inline void lgAudio_setTransport(
|
||||
const LG_AudioOps * ops, void * opaque) {}
|
||||
static inline void lgAudio_dropTransport(void) {}
|
||||
|
||||
@@ -482,8 +482,8 @@ static const LG_ClipboardEventOps eventOps =
|
||||
.request = eventRequest,
|
||||
};
|
||||
|
||||
/* providerLock must be held. dropActive suppresses remote release when the
|
||||
* endpoint has already disappeared; detach must always quiesce local events. */
|
||||
/* providerLock must be held. dropActive suppresses all calls into an endpoint
|
||||
* which has already disappeared. */
|
||||
static void updateActive(bool dropActive)
|
||||
{
|
||||
for (;;)
|
||||
@@ -501,9 +501,9 @@ static void updateActive(bool dropActive)
|
||||
clipboard.active = (ClipboardBinding) { 0 };
|
||||
LG_UNLOCK_EXCLUSIVE(clipboard.activeLock);
|
||||
|
||||
if (old.ops)
|
||||
if (old.ops && !dropActive)
|
||||
{
|
||||
if (!dropActive && !old.ops->release(old.opaque))
|
||||
if (!old.ops->release(old.opaque))
|
||||
DEBUG_WARN("Failed to release Clipboard provider: %s",
|
||||
old.ops->name);
|
||||
old.ops->detach(old.opaque);
|
||||
@@ -687,6 +687,24 @@ void lgClipboard_setFallback(const LG_ClipboardOps * ops, void * opaque)
|
||||
ops, opaque, fallbackStatusChanged);
|
||||
}
|
||||
|
||||
static void dropBinding(ClipboardBinding * target)
|
||||
{
|
||||
LG_LOCK(clipboard.registrationLock);
|
||||
LG_LOCK(clipboard.providerLock);
|
||||
LG_LOCK_EXCLUSIVE(clipboard.activeLock);
|
||||
const bool wasActive = bindingActiveNL(target);
|
||||
*target = (ClipboardBinding) { 0 };
|
||||
LG_UNLOCK_EXCLUSIVE(clipboard.activeLock);
|
||||
updateActive(wasActive);
|
||||
LG_UNLOCK(clipboard.providerLock);
|
||||
LG_UNLOCK(clipboard.registrationLock);
|
||||
}
|
||||
|
||||
void lgClipboard_dropFallback(void)
|
||||
{
|
||||
dropBinding(&clipboard.fallback);
|
||||
}
|
||||
|
||||
void lgClipboard_setTransport(const LG_ClipboardOps * ops, void * opaque)
|
||||
{
|
||||
setBinding(&clipboard.transport,
|
||||
@@ -695,22 +713,7 @@ void lgClipboard_setTransport(const LG_ClipboardOps * ops, void * opaque)
|
||||
|
||||
void lgClipboard_dropTransport(void)
|
||||
{
|
||||
LG_LOCK(clipboard.registrationLock);
|
||||
LG_LOCK(clipboard.providerLock);
|
||||
const ClipboardBinding old = clipboard.transport;
|
||||
LG_UNLOCK(clipboard.providerLock);
|
||||
|
||||
if (old.ops && old.ops->setStatusListener)
|
||||
old.ops->setStatusListener(old.opaque, NULL, NULL);
|
||||
|
||||
LG_LOCK(clipboard.providerLock);
|
||||
LG_LOCK_EXCLUSIVE(clipboard.activeLock);
|
||||
const bool wasActive = bindingActiveNL(&clipboard.transport);
|
||||
clipboard.transport = (ClipboardBinding) { 0 };
|
||||
LG_UNLOCK_EXCLUSIVE(clipboard.activeLock);
|
||||
updateActive(wasActive);
|
||||
LG_UNLOCK(clipboard.providerLock);
|
||||
LG_UNLOCK(clipboard.registrationLock);
|
||||
dropBinding(&clipboard.transport);
|
||||
}
|
||||
|
||||
void lgClipboard_release(void)
|
||||
|
||||
@@ -28,6 +28,8 @@ void lgClipboard_free(void);
|
||||
void lgClipboard_setLocalAvailable(bool available);
|
||||
|
||||
void lgClipboard_setFallback(const LG_ClipboardOps * ops, void * opaque);
|
||||
/* Drop functions remove a dead endpoint without invoking it. */
|
||||
void lgClipboard_dropFallback(void);
|
||||
void lgClipboard_setTransport(const LG_ClipboardOps * ops, void * opaque);
|
||||
void lgClipboard_dropTransport(void);
|
||||
|
||||
|
||||
@@ -32,12 +32,14 @@ struct InputBinding
|
||||
void * opaque;
|
||||
bool available;
|
||||
bool mouseAbsolute;
|
||||
uint32_t generation;
|
||||
uint32_t epoch;
|
||||
};
|
||||
|
||||
static struct
|
||||
{
|
||||
LG_Lock bindingLock;
|
||||
LG_RWLock activeLock;
|
||||
uint32_t nextBindingEpoch;
|
||||
|
||||
struct InputBinding fallback;
|
||||
struct InputBinding transport;
|
||||
@@ -66,6 +68,14 @@ static struct InputBinding makeBinding(const LG_InputOps * ops,
|
||||
void * opaque)
|
||||
{
|
||||
const bool available = ops && !ops->setStatusListener;
|
||||
uint32_t epoch = 0;
|
||||
if (ops)
|
||||
{
|
||||
epoch = ++l_input.nextBindingEpoch;
|
||||
if (!epoch)
|
||||
epoch = ++l_input.nextBindingEpoch;
|
||||
}
|
||||
|
||||
return (struct InputBinding)
|
||||
{
|
||||
.ops = ops,
|
||||
@@ -73,10 +83,18 @@ static struct InputBinding makeBinding(const LG_InputOps * ops,
|
||||
.available = available,
|
||||
.mouseAbsolute = available && ops->mousePosition &&
|
||||
ops->supports(opaque, LG_INPUT_SUPPORT_MOUSE_ABSOLUTE),
|
||||
.generation = 0,
|
||||
.epoch = epoch,
|
||||
};
|
||||
}
|
||||
|
||||
static bool bindingEqual(const struct InputBinding * a,
|
||||
const struct InputBinding * b)
|
||||
{
|
||||
return a->ops == b->ops &&
|
||||
a->opaque == b->opaque &&
|
||||
a->epoch == b->epoch;
|
||||
}
|
||||
|
||||
static void releaseKeysNL(void)
|
||||
{
|
||||
for (int key = 0; key < KEY_MAX; ++key)
|
||||
@@ -114,7 +132,7 @@ static void clearStateNL(void)
|
||||
atomic_store_explicit(&l_input.buttons, 0, memory_order_relaxed);
|
||||
}
|
||||
|
||||
static void updateActiveNL(void)
|
||||
static void updateActiveNL(bool dropActive)
|
||||
{
|
||||
const struct InputBinding next =
|
||||
l_input.useTransport && l_input.transport.available ?
|
||||
@@ -122,13 +140,15 @@ static void updateActiveNL(void)
|
||||
l_input.fallback.available ? l_input.fallback :
|
||||
(struct InputBinding) { 0 };
|
||||
|
||||
if (next.ops == l_input.active.ops &&
|
||||
next.opaque == l_input.active.opaque)
|
||||
if (bindingEqual(&next, &l_input.active))
|
||||
{
|
||||
l_input.active = next;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dropActive)
|
||||
clearStateNL();
|
||||
else
|
||||
resetActiveNL();
|
||||
l_input.active = next;
|
||||
|
||||
@@ -145,9 +165,7 @@ static void updateActiveNL(void)
|
||||
static void updateStatusNL(struct InputBinding * binding,
|
||||
const LG_InputStatus * status)
|
||||
{
|
||||
const bool wasActive =
|
||||
l_input.active.ops == binding->ops &&
|
||||
l_input.active.opaque == binding->opaque;
|
||||
const bool wasActive = bindingEqual(&l_input.active, binding);
|
||||
|
||||
if (wasActive && !status->available)
|
||||
{
|
||||
@@ -160,8 +178,7 @@ static void updateStatusNL(struct InputBinding * binding,
|
||||
binding->ops->mousePosition &&
|
||||
binding->ops->supports(binding->opaque,
|
||||
LG_INPUT_SUPPORT_MOUSE_ABSOLUTE);
|
||||
binding->generation = status->generation;
|
||||
updateActiveNL();
|
||||
updateActiveNL(false);
|
||||
}
|
||||
|
||||
static void fallbackStatusChanged(void * opaque,
|
||||
@@ -171,7 +188,8 @@ static void fallbackStatusChanged(void * opaque,
|
||||
return;
|
||||
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
if (l_input.fallback.ops && l_input.fallback.opaque == opaque)
|
||||
if (l_input.fallback.ops &&
|
||||
l_input.fallback.epoch == (uint32_t)(uintptr_t)opaque)
|
||||
updateStatusNL(&l_input.fallback, status);
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
}
|
||||
@@ -183,7 +201,8 @@ static void transportStatusChanged(void * opaque,
|
||||
return;
|
||||
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
if (l_input.transport.ops && l_input.transport.opaque == opaque)
|
||||
if (l_input.transport.ops &&
|
||||
l_input.transport.epoch == (uint32_t)(uintptr_t)opaque)
|
||||
updateStatusNL(&l_input.transport, status);
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
}
|
||||
@@ -199,6 +218,7 @@ void lgInput_init(void)
|
||||
atomic_init(&l_input.keys[key], false);
|
||||
atomic_init(&l_input.buttons, 0);
|
||||
|
||||
LG_LOCK_INIT(l_input.bindingLock);
|
||||
LG_RWLOCK_INIT(l_input.activeLock);
|
||||
}
|
||||
|
||||
@@ -207,6 +227,7 @@ void lgInput_free(void)
|
||||
struct InputBinding fallback;
|
||||
struct InputBinding transport;
|
||||
|
||||
LG_LOCK(l_input.bindingLock);
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
resetActiveNL();
|
||||
fallback = l_input.fallback;
|
||||
@@ -221,85 +242,79 @@ void lgInput_free(void)
|
||||
if (transport.ops && transport.ops->setStatusListener)
|
||||
transport.ops->setStatusListener(transport.opaque, NULL, NULL);
|
||||
|
||||
LG_UNLOCK(l_input.bindingLock);
|
||||
LG_RWLOCK_FREE(l_input.activeLock);
|
||||
LG_LOCK_FREE(l_input.bindingLock);
|
||||
}
|
||||
|
||||
static void setBinding(struct InputBinding * target,
|
||||
const LG_InputOps * ops, void * opaque, LG_InputStatusFn statusFn)
|
||||
{
|
||||
if (ops && !validOps(ops))
|
||||
{
|
||||
DEBUG_ERROR("Invalid input operations");
|
||||
ops = NULL;
|
||||
opaque = NULL;
|
||||
}
|
||||
|
||||
struct InputBinding old;
|
||||
|
||||
LG_LOCK(l_input.bindingLock);
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
old = *target;
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
|
||||
if (old.ops && old.ops->setStatusListener)
|
||||
old.ops->setStatusListener(old.opaque, NULL, NULL);
|
||||
|
||||
const struct InputBinding next = makeBinding(ops, opaque);
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
*target = next;
|
||||
updateActiveNL(false);
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
|
||||
if (next.ops && next.ops->setStatusListener)
|
||||
next.ops->setStatusListener(next.opaque, statusFn,
|
||||
(void *)(uintptr_t)next.epoch);
|
||||
LG_UNLOCK(l_input.bindingLock);
|
||||
}
|
||||
|
||||
static void dropBinding(struct InputBinding * target)
|
||||
{
|
||||
LG_LOCK(l_input.bindingLock);
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
const bool wasActive = bindingEqual(&l_input.active, target);
|
||||
*target = (struct InputBinding) { 0 };
|
||||
updateActiveNL(wasActive);
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
LG_UNLOCK(l_input.bindingLock);
|
||||
}
|
||||
|
||||
void lgInput_setFallback(const LG_InputOps * ops, void * opaque)
|
||||
{
|
||||
if (ops && !validOps(ops))
|
||||
{
|
||||
DEBUG_ERROR("Invalid fallback input operations");
|
||||
ops = NULL;
|
||||
opaque = NULL;
|
||||
setBinding(&l_input.fallback, ops, opaque, fallbackStatusChanged);
|
||||
}
|
||||
|
||||
const struct InputBinding next = makeBinding(ops, opaque);
|
||||
struct InputBinding old;
|
||||
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
old = l_input.fallback;
|
||||
l_input.fallback = next;
|
||||
updateActiveNL();
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
|
||||
if (old.ops && old.ops->setStatusListener)
|
||||
old.ops->setStatusListener(old.opaque, NULL, NULL);
|
||||
if (next.ops && next.ops->setStatusListener)
|
||||
next.ops->setStatusListener(next.opaque,
|
||||
fallbackStatusChanged, next.opaque);
|
||||
void lgInput_dropFallback(void)
|
||||
{
|
||||
dropBinding(&l_input.fallback);
|
||||
}
|
||||
|
||||
void lgInput_setTransport(const LG_InputOps * ops, void * opaque)
|
||||
{
|
||||
if (ops && !validOps(ops))
|
||||
{
|
||||
DEBUG_ERROR("Invalid transport input operations");
|
||||
ops = NULL;
|
||||
opaque = NULL;
|
||||
}
|
||||
|
||||
const struct InputBinding next = makeBinding(ops, opaque);
|
||||
struct InputBinding old;
|
||||
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
old = l_input.transport;
|
||||
l_input.transport = next;
|
||||
updateActiveNL();
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
|
||||
if (old.ops && old.ops->setStatusListener)
|
||||
old.ops->setStatusListener(old.opaque, NULL, NULL);
|
||||
if (next.ops && next.ops->setStatusListener)
|
||||
next.ops->setStatusListener(next.opaque,
|
||||
transportStatusChanged, next.opaque);
|
||||
setBinding(&l_input.transport, ops, opaque, transportStatusChanged);
|
||||
}
|
||||
|
||||
void lgInput_dropTransport(void)
|
||||
{
|
||||
struct InputBinding old;
|
||||
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
old = l_input.transport;
|
||||
if (l_input.useTransport && l_input.active.ops == l_input.transport.ops &&
|
||||
l_input.active.opaque == l_input.transport.opaque)
|
||||
{
|
||||
l_input.active = (struct InputBinding) { 0 };
|
||||
clearStateNL();
|
||||
}
|
||||
|
||||
l_input.transport = (struct InputBinding) { 0 };
|
||||
updateActiveNL();
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
|
||||
if (old.ops && old.ops->setStatusListener)
|
||||
old.ops->setStatusListener(old.opaque, NULL, NULL);
|
||||
dropBinding(&l_input.transport);
|
||||
}
|
||||
|
||||
void lgInput_useTransport(bool enable)
|
||||
{
|
||||
LG_LOCK_EXCLUSIVE(l_input.activeLock);
|
||||
l_input.useTransport = enable;
|
||||
updateActiveNL();
|
||||
updateActiveNL(false);
|
||||
LG_UNLOCK_EXCLUSIVE(l_input.activeLock);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user