540grunkspin
08/12/2019, 1:58 PMDominaezzz
08/12/2019, 2:14 PMCompletableDeferred
might be what you're looking for.540grunkspin
08/12/2019, 2:18 PMDominaezzz
08/12/2019, 2:21 PM540grunkspin
08/12/2019, 2:23 PMDominaezzz
08/12/2019, 2:24 PM540grunkspin
08/12/2019, 2:24 PMDominaezzz
08/12/2019, 2:28 PM// This has internal threadpool
val api = TODO("Get super secret api. :) ")
// 1) This suspends
api.submitTask { controller ->
// Do stuff with connected controller.
}
// 2) This suspends
val controller = api.getController()
// Do stuff with connected controller.
540grunkspin
08/12/2019, 2:30 PMDominaezzz
08/12/2019, 2:31 PMsendCommand
will use the controller internally. The user won't touch the controller directly.540grunkspin
08/12/2019, 2:31 PMonConnected
and an onDisconnected
methodDominaezzz
08/12/2019, 2:33 PMonConnected
you signal the internal threadpool to stop waiting and use the controller.onDisconnected
?onConnected
is called again?540grunkspin
08/12/2019, 2:36 PMfun sendCommand() {
lock.lock()
try {
while(!controller.isConnected) {
cond.await()
}
controller.runCommand()
} finally {
lock.unlock()
}
}
Dominaezzz
08/12/2019, 2:38 PMConflatedBroadcastChannel
.540grunkspin
08/12/2019, 2:38 PMsuspendCoroutine
so that way the user can use coroutines but internally it is threadsDominaezzz
08/12/2019, 2:40 PMfun sendCommand() {
val input = channel.openSubscription()
try {
input.receive()
controller.runCommand()
} finally {
input.cancel()
}
}
540grunkspin
08/12/2019, 2:40 PMDominaezzz
08/12/2019, 2:41 PMConflatedBroadcastChannel
is designed to remember the last sent element.540grunkspin
08/12/2019, 2:41 PMDominaezzz
08/12/2019, 2:43 PMfun onConnected() {
channel.offer(Unit)
}
540grunkspin
08/12/2019, 2:43 PMDominaezzz
08/12/2019, 2:44 PM540grunkspin
08/12/2019, 2:44 PMDominaezzz
08/12/2019, 2:44 PMConflatedBroadcastChannel<Controller?>
then yes.540grunkspin
08/12/2019, 2:45 PMDominaezzz
08/12/2019, 2:45 PM540grunkspin
08/12/2019, 2:45 PMDominaezzz
08/12/2019, 2:45 PM540grunkspin
08/12/2019, 2:46 PMDominaezzz
08/12/2019, 2:47 PM540grunkspin
08/12/2019, 2:47 PMDominaezzz
08/12/2019, 2:47 PM540grunkspin
08/12/2019, 2:49 PMDominaezzz
08/12/2019, 2:51 PMConflatedBroadcastChannel
in that case.sendCommand
you can pass a some data into a channel.540grunkspin
08/12/2019, 2:53 PMDominaezzz
08/12/2019, 2:54 PMAtomicBoolean
to keep track.540grunkspin
08/12/2019, 2:55 PMDominaezzz
08/12/2019, 2:56 PM540grunkspin
08/12/2019, 2:56 PMDominaezzz
08/12/2019, 2:56 PM540grunkspin
08/12/2019, 2:57 PMDominaezzz
08/12/2019, 2:58 PM540grunkspin
08/12/2019, 2:59 PMDominaezzz
08/12/2019, 2:59 PMConflatedBroadcastChannel
are still experimental but luckily you don't need it anymore. 🙂540grunkspin
08/12/2019, 3:00 PMDominaezzz
08/12/2019, 3:01 PMChannel(capacity = CONFLATED)
.540grunkspin
08/12/2019, 3:01 PMDominaezzz
08/12/2019, 3:04 PMwithTimeout(1000) { api.sendCommand(...) }
.540grunkspin
08/12/2019, 3:17 PMDominaezzz
08/12/2019, 3:18 PM540grunkspin
08/12/2019, 3:18 PMDominaezzz
08/12/2019, 3:19 PM540grunkspin
08/12/2019, 3:20 PMDominaezzz
08/12/2019, 3:21 PM540grunkspin
08/12/2019, 3:23 PMDominaezzz
08/12/2019, 3:24 PMonConnected
call complete
on the current CompletableDeferred
and in onDisconnected
send a new CompletableDeferred
.540grunkspin
08/12/2019, 3:25 PMDominaezzz
08/12/2019, 3:25 PMAtomicBoolean
won't let you wait.poll()
to see if there's a new state change, if the new value is true
then you can continue processing, if the new value is false
you then call receive()
to wait.540grunkspin
08/12/2019, 3:29 PMval isConnected = AtomicBoolean(false)
eventChannel.takeIf { isConnected.get() }.onReceive {event ->
try {
controller.handleEvent()
} finally {
if (!isConnected.get()) {
eventChannel.send(event)
}
}
}
Dominaezzz
08/12/2019, 3:32 PM540grunkspin
08/12/2019, 3:33 PMif (isConnectedChannel.poll() != true) {
while (!isConnectedChannel.receive()) { }
}
while (!isConnectedChannel.receive()) {}
Dominaezzz
08/12/2019, 3:37 PM540grunkspin
08/12/2019, 3:38 PMif (isConnectedChannel.poll() != true) {
while (!isConnectedChannel.receive()) {}
}
Dominaezzz
08/12/2019, 3:40 PM540grunkspin
08/12/2019, 3:42 PMif (isConnectedChannel.poll() != true) {
while (!isConnectedChannel.receive()) { }
isConnectedChannel.send(true)
}
Dominaezzz
08/12/2019, 3:43 PM540grunkspin
08/12/2019, 3:44 PMDominaezzz
08/12/2019, 3:44 PMonConnected
and onDisconnected
.540grunkspin
08/12/2019, 3:45 PMDominaezzz
08/12/2019, 3:47 PM540grunkspin
08/12/2019, 3:47 PMDominaezzz
08/12/2019, 3:48 PM540grunkspin
08/12/2019, 3:52 PMDominaezzz
08/12/2019, 3:53 PMFlow
is an option.540grunkspin
08/12/2019, 3:53 PMDominaezzz
08/12/2019, 3:56 PM540grunkspin
08/12/2019, 3:57 PMcombine
and a takeWhile
Dominaezzz
08/12/2019, 3:57 PMonConnected
, onDisconnected
, etc.540grunkspin
08/12/2019, 3:58 PMDominaezzz
08/12/2019, 4:01 PM540grunkspin
08/12/2019, 4:07 PMDominaezzz
08/12/2019, 4:08 PMcallbackFlow<Boolean> { channel ->
val controller = TODO("")
controller.onConnected {
channel.offer(true)
}
controller.onDisconnected {
channel.offer(false)
}
awaitClose { controller.close() }
}
540grunkspin
08/12/2019, 4:09 PMDominaezzz
08/12/2019, 4:28 PMFlow
and the Channel
solution is getting out of hand. The original ConflatedBroadcastChannel
will do the trick.val connectionChannel = ConflatedBroadcastChannel<Boolean>()
GlobalScope.launch {
while (isActive) {
if (!connectionChannel.value) {
val channel = connectionChannel.openSubscription()
while (!channel.receive());
channel.cancel()
}
try {
// Handle event
} finally {
// Requeue if necessary.
}
}
}
540grunkspin
08/12/2019, 5:17 PMDominaezzz
08/12/2019, 5:30 PM540grunkspin
08/13/2019, 7:44 AMDominaezzz
08/13/2019, 9:24 AM