@@ -78,6 +78,9 @@ public final class LinuxContainer: Container, Sendable {
7878 /// Run the container with a minimal init process that handles signal
7979 /// forwarding and zombie reaping.
8080 public var useInit : Bool = false
81+ /// Interval for syncing the guest clock with the host. Set to nil
82+ /// to disable time synchronization.
83+ public var timeSyncInterval : Duration ? = . seconds( 20 )
8184
8285 public init ( ) { }
8386
@@ -95,7 +98,8 @@ public final class LinuxContainer: Container, Sendable {
9598 virtualization: Bool = false ,
9699 bootLog: BootLog ? = nil ,
97100 ociRuntimePath: String ? = nil ,
98- useInit: Bool = false
101+ useInit: Bool = false ,
102+ timeSyncInterval: Duration ? = . seconds( 20 )
99103 ) {
100104 self . process = process
101105 self . cpus = cpus
@@ -111,6 +115,7 @@ public final class LinuxContainer: Container, Sendable {
111115 self . bootLog = bootLog
112116 self . ociRuntimePath = ociRuntimePath
113117 self . useInit = useInit
118+ self . timeSyncInterval = timeSyncInterval
114119 }
115120 }
116121
@@ -145,6 +150,7 @@ public final class LinuxContainer: Container, Sendable {
145150 let vm : any VirtualMachineInstance
146151 let relayManager : UnixSocketRelayManager
147152 var fileMountContext : FileMountContext
153+ let timeSyncer : TimeSyncer ?
148154 }
149155
150156 struct StartedState : Sendable {
@@ -153,13 +159,15 @@ public final class LinuxContainer: Container, Sendable {
153159 let relayManager : UnixSocketRelayManager
154160 var vendedProcesses : [ String : LinuxProcess ]
155161 let fileMountContext : FileMountContext
162+ let timeSyncer : TimeSyncer ?
156163
157164 init ( _ state: CreatedState , process: LinuxProcess ) {
158165 self . vm = state. vm
159166 self . relayManager = state. relayManager
160167 self . process = process
161168 self . vendedProcesses = [ : ]
162169 self . fileMountContext = state. fileMountContext
170+ self . timeSyncer = state. timeSyncer
163171 }
164172
165173 init ( _ state: PausedState ) {
@@ -168,6 +176,7 @@ public final class LinuxContainer: Container, Sendable {
168176 self . process = state. process
169177 self . vendedProcesses = state. vendedProcesses
170178 self . fileMountContext = state. fileMountContext
179+ self . timeSyncer = state. timeSyncer
171180 }
172181 }
173182
@@ -177,13 +186,15 @@ public final class LinuxContainer: Container, Sendable {
177186 let process : LinuxProcess
178187 var vendedProcesses : [ String : LinuxProcess ]
179188 let fileMountContext : FileMountContext
189+ let timeSyncer : TimeSyncer ?
180190
181191 init ( _ state: StartedState ) {
182192 self . vm = state. vm
183193 self . relayManager = state. relayManager
184194 self . process = state. process
185195 self . vendedProcesses = state. vendedProcesses
186196 self . fileMountContext = state. fileMountContext
197+ self . timeSyncer = state. timeSyncer
187198 }
188199 }
189200
@@ -622,7 +633,16 @@ extension LinuxContainer {
622633 }
623634
624635 }
625- state = . created( . init( vm: vm, relayManager: relayManager, fileMountContext: fileMountContextHolder. withLock { $0 } ) )
636+
637+ var timeSyncer : TimeSyncer ?
638+ if let interval = self . config. timeSyncInterval {
639+ let ts = TimeSyncer ( logger: self . logger)
640+ let tsAgent = try await vm. dialAgent ( )
641+ await ts. start ( context: tsAgent, interval: interval)
642+ timeSyncer = ts
643+ }
644+
645+ state = . created( . init( vm: vm, relayManager: relayManager, fileMountContext: fileMountContextHolder. withLock { $0 } , timeSyncer: timeSyncer) )
626646 } catch {
627647 try ? await relayManager. stopAll ( )
628648 try ? await vm. stop ( )
@@ -719,18 +739,28 @@ extension LinuxContainer {
719739
720740 let vm : any VirtualMachineInstance
721741 let relayManager : UnixSocketRelayManager
742+ let timeSyncer : TimeSyncer ?
722743
723744 let startedState = try ? state. startedState ( " stop " )
724745 if let startedState {
725746 vm = startedState. vm
726747 relayManager = startedState. relayManager
748+ timeSyncer = startedState. timeSyncer
727749 } else {
728750 let createdState = try state. createdState ( " stop " )
729751 vm = createdState. vm
730752 relayManager = createdState. relayManager
753+ timeSyncer = createdState. timeSyncer
731754 }
732755
733756 var firstError : Error ?
757+ do {
758+ try await timeSyncer? . close ( )
759+ } catch {
760+ self . logger? . error ( " failed to close time syncer: \( error) " )
761+ firstError = firstError ?? error
762+ }
763+
734764 do {
735765 try await relayManager. stopAll ( )
736766 } catch {
0 commit comments