diff --git a/lib/system/api/containers.toit b/lib/system/api/containers.toit index 1a9e3f013..92e38523a 100644 --- a/lib/system/api/containers.toit +++ b/lib/system/api/containers.toit @@ -39,6 +39,9 @@ interface ContainerService: image-writer-commit handle/int flags/int data/int -> uuid.Uuid static IMAGE-WRITER-COMMIT-INDEX /int ::= 5 + notify-background-state-changed new-state/any -> none + static NOTIFY-BACKGROUND-STATE-CHANGED-INDEX /int ::= 8 + class ContainerServiceClient extends ServiceClient implements ContainerService: static SELECTOR ::= ContainerService.SELECTOR constructor selector/ServiceSelector=SELECTOR: @@ -75,3 +78,6 @@ class ContainerServiceClient extends ServiceClient implements ContainerService: image-writer-commit handle/int flags/int data/int -> uuid.Uuid: return uuid.Uuid (invoke_ ContainerService.IMAGE-WRITER-COMMIT-INDEX [handle, flags, data]) + + notify-background-state-changed new-state/bool -> none: + invoke_ ContainerService.NOTIFY-BACKGROUND-STATE-CHANGED-INDEX new-state diff --git a/lib/system/containers.toit b/lib/system/containers.toit index d288e4977..44465da83 100644 --- a/lib/system/containers.toit +++ b/lib/system/containers.toit @@ -43,6 +43,10 @@ start id/uuid.Uuid arguments/any=[] -> Container: uninstall id/uuid.Uuid -> none: _client_.uninstall-image id +/** Notifies the system about a background-state change. */ +notify-background-state-changed new-state/bool -> none: + _client_.notify-background-state-changed new-state + class ContainerImage: id/uuid.Uuid name/string? @@ -51,6 +55,8 @@ class ContainerImage: constructor --.id --.name --.flags --.data: class Container extends ServiceResourceProxy: + static EVENT-BACKGROUND-STATE-CHANGE ::= 0 + // TODO(kasper): Rename this and document it. id/uuid.Uuid @@ -70,6 +76,7 @@ class Container extends ServiceResourceProxy: result_/monitor.Latch ::= monitor.Latch on-stopped_/Lambda? := null + on-event_/Lambda? := null constructor.internal_ --handle/int --.id --.gid: super _client_ handle @@ -101,14 +108,30 @@ class Container extends ServiceResourceProxy: else: on-stopped_ = lambda - on-notified_ code/int -> none: - result_.set code - on-stopped := on-stopped_ - on-stopped_ = null - if on-stopped: on-stopped.call code - // We no longer expect or care about notifications, so - // close the resource. - close + on-event lambda/Lambda? -> none: + if not lambda: + on-event_ = null + return + if on-event_: throw "ALREADY_IN_USE" + on-event_ = lambda + + on-notified_ notification/any -> none: + if notification is int: + code/int := notification + result_.set code + on-stopped := on-stopped_ + on-stopped_ = null + if on-stopped: on-stopped.call code + // We no longer expect or care about notifications, so + // close the resource. + close + else if on-event_: + if notification is not List or notification.size != 2 or notification[0] is not int: + // Discard unknown event. + return + event-kind := notification[0] + event-value := notification[1] + on-event_.call event-kind event-value class ContainerImageWriter extends ServiceResourceProxy: size/int ::= ? diff --git a/system/containers.toit b/system/containers.toit index dba158261..2c11cedc2 100644 --- a/system/containers.toit +++ b/system/containers.toit @@ -23,6 +23,7 @@ import encoding.tison import system.assets import system.services show ServiceHandler ServiceProvider ServiceResource import system.api.containers show ContainerService +import system.containers as system-containers import .flash.allocation import .flash.image-writer @@ -45,8 +46,7 @@ class Container: start arguments/any=image.default-arguments -> none: if pids_: throw "Already started" - pids_ = {} - pids_.add (image.spawn this arguments) + pids_ = {image.spawn this arguments} stop -> none: if not pids_: throw "Not started" @@ -56,6 +56,11 @@ class Container: image.manager.on-container-stop_ this 0 resources.do: it.on-container-stop 0 + send-event event/any: + if not pids_: throw "Not started" + if pids_.is-empty: return + resources.do: it.send-event event + on-stop_ -> none: pids_.do: on-process-stop_ it 0 @@ -91,6 +96,10 @@ class ContainerResource extends ServiceResource: if is-closed: return notify_ code + send-event event/List -> none: + if is-closed: return + notify_ event + on-closed -> none: container.resources.remove this @@ -177,6 +186,7 @@ class ContainerImageFlash extends ContainerImage: abstract class ContainerServiceProvider extends ServiceProvider implements ContainerService ServiceHandler: + constructor: super "system/containers" --major=0 --minor=2 provides ContainerService.SELECTOR --handler=this @@ -203,12 +213,17 @@ abstract class ContainerServiceProvider extends ServiceProvider if index == ContainerService.IMAGE-WRITER-COMMIT-INDEX: writer ::= (resource client arguments[0]) as ContainerImageWriter return (image-writer-commit writer arguments[1] arguments[2]).to-byte-array + if index == ContainerService.NOTIFY-BACKGROUND-STATE-CHANGED-INDEX: + return send-container-event --gid=gid + system-containers.Container.EVENT-BACKGROUND-STATE-CHANGE + arguments unreachable abstract image-registry -> FlashRegistry abstract images -> List abstract add-flash-image allocation/FlashAllocation -> ContainerImage abstract lookup-image id/uuid.Uuid -> ContainerImage? + abstract send-container-event --gid/int event-kind/int event-value/any -> none list-images -> List: names := {:} @@ -263,6 +278,9 @@ abstract class ContainerServiceProvider extends ServiceProvider image := add-flash-image allocation return image.id + notify-background-state-changed new-state/bool: + unreachable // Here to satisfy the checker. + class ContainerManager extends ContainerServiceProvider implements SystemMessageHandler_: image-registry/FlashRegistry ::= ? service-manager_/SystemServiceManager ::= ? @@ -366,6 +384,10 @@ class ContainerManager extends ContainerServiceProvider implements SystemMessage else: unreachable + send-container-event --gid/int event-kind/int event-value/any -> none: + container/Container? := lookup-container gid + if container: container.send-event [event-kind, event-value] + trace-using-print message/ByteArray --from=0 --to=message.size: // Print a trace message on output so that that you can easily decode. // The message is base64 encoded to limit the output size. diff --git a/tests/containers-test.toit b/tests/containers-test.toit index f7818610e..d3c1b35a8 100644 --- a/tests/containers-test.toit +++ b/tests/containers-test.toit @@ -2,6 +2,7 @@ // Use of this source code is governed by a Zero-Clause BSD license that can // be found in the tests/LICENSE file. +import monitor import system.containers import expect show * @@ -15,6 +16,7 @@ main arguments: test-images test-start + test-background-state-changed test-images: images/List := containers.images @@ -58,5 +60,19 @@ test-start: expect-equals 0 lambda4-value expect-equals 0 sub4.wait +test-background-state-changed: + sub := containers.start containers.current { "background-state-test": true } + channel := monitor.Channel 1 + sub.on-event:: | event-id/int value | + expect-equals containers.Container.EVENT-BACKGROUND-STATE-CHANGE event-id + channel.send value + + expect_equals true channel.receive + expect_equals false channel.receive + main-child arguments/Map: + if arguments.contains "background-state-test": + sleep --ms=10 + containers.notify-background-state-changed true + containers.notify-background-state-changed false sleep --ms=100 diff --git a/tests/negative/gold/illegal-system-call-test.gold b/tests/negative/gold/illegal-system-call-test.gold index e82aa1c03..007e41a3a 100644 --- a/tests/negative/gold/illegal-system-call-test.gold +++ b/tests/negative/gold/illegal-system-call-test.gold @@ -1,6 +1,6 @@ As check failed: null is not a ServiceResource. 0: ServiceProvider.resource /system/services.toit:401:5 - 1: ContainerServiceProvider.handle <...>/system/containers.toit:201:19 + 1: ContainerServiceProvider.handle <...>/system/containers.toit:211:19 2: ServiceManager_. /system/services.toit:634:15 3: RpcRequest_.process. /rpc/broker.toit:98:26 4: RpcRequest_.process /rpc/broker.toit:95:3