system.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. package hcs
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "os"
  7. "strconv"
  8. "strings"
  9. "sync"
  10. "syscall"
  11. "time"
  12. "github.com/Microsoft/hcsshim/internal/cow"
  13. "github.com/Microsoft/hcsshim/internal/log"
  14. "github.com/Microsoft/hcsshim/internal/oc"
  15. "github.com/Microsoft/hcsshim/internal/schema1"
  16. hcsschema "github.com/Microsoft/hcsshim/internal/schema2"
  17. "github.com/Microsoft/hcsshim/internal/timeout"
  18. "github.com/Microsoft/hcsshim/internal/vmcompute"
  19. "go.opencensus.io/trace"
  20. )
  21. // currentContainerStarts is used to limit the number of concurrent container
  22. // starts.
  23. var currentContainerStarts containerStarts
  24. type containerStarts struct {
  25. maxParallel int
  26. inProgress int
  27. sync.Mutex
  28. }
  29. func init() {
  30. mpsS := os.Getenv("HCSSHIM_MAX_PARALLEL_START")
  31. if len(mpsS) > 0 {
  32. mpsI, err := strconv.Atoi(mpsS)
  33. if err != nil || mpsI < 0 {
  34. return
  35. }
  36. currentContainerStarts.maxParallel = mpsI
  37. }
  38. }
  39. type System struct {
  40. handleLock sync.RWMutex
  41. handle vmcompute.HcsSystem
  42. id string
  43. callbackNumber uintptr
  44. closedWaitOnce sync.Once
  45. waitBlock chan struct{}
  46. waitError error
  47. exitError error
  48. os, typ string
  49. }
  50. func newSystem(id string) *System {
  51. return &System{
  52. id: id,
  53. waitBlock: make(chan struct{}),
  54. }
  55. }
  56. // CreateComputeSystem creates a new compute system with the given configuration but does not start it.
  57. func CreateComputeSystem(ctx context.Context, id string, hcsDocumentInterface interface{}) (_ *System, err error) {
  58. operation := "hcsshim::CreateComputeSystem"
  59. // hcsCreateComputeSystemContext is an async operation. Start the outer span
  60. // here to measure the full create time.
  61. ctx, span := trace.StartSpan(ctx, operation)
  62. defer span.End()
  63. defer func() { oc.SetSpanStatus(span, err) }()
  64. span.AddAttributes(trace.StringAttribute("cid", id))
  65. computeSystem := newSystem(id)
  66. hcsDocumentB, err := json.Marshal(hcsDocumentInterface)
  67. if err != nil {
  68. return nil, err
  69. }
  70. hcsDocument := string(hcsDocumentB)
  71. var (
  72. identity syscall.Handle
  73. resultJSON string
  74. createError error
  75. )
  76. computeSystem.handle, resultJSON, createError = vmcompute.HcsCreateComputeSystem(ctx, id, hcsDocument, identity)
  77. if createError == nil || IsPending(createError) {
  78. defer func() {
  79. if err != nil {
  80. computeSystem.Close()
  81. }
  82. }()
  83. if err = computeSystem.registerCallback(ctx); err != nil {
  84. // Terminate the compute system if it still exists. We're okay to
  85. // ignore a failure here.
  86. computeSystem.Terminate(ctx)
  87. return nil, makeSystemError(computeSystem, operation, "", err, nil)
  88. }
  89. }
  90. events, err := processAsyncHcsResult(ctx, createError, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemCreateCompleted, &timeout.SystemCreate)
  91. if err != nil {
  92. if err == ErrTimeout {
  93. // Terminate the compute system if it still exists. We're okay to
  94. // ignore a failure here.
  95. computeSystem.Terminate(ctx)
  96. }
  97. return nil, makeSystemError(computeSystem, operation, hcsDocument, err, events)
  98. }
  99. go computeSystem.waitBackground()
  100. if err = computeSystem.getCachedProperties(ctx); err != nil {
  101. return nil, err
  102. }
  103. return computeSystem, nil
  104. }
  105. // OpenComputeSystem opens an existing compute system by ID.
  106. func OpenComputeSystem(ctx context.Context, id string) (*System, error) {
  107. operation := "hcsshim::OpenComputeSystem"
  108. computeSystem := newSystem(id)
  109. handle, resultJSON, err := vmcompute.HcsOpenComputeSystem(ctx, id)
  110. events := processHcsResult(ctx, resultJSON)
  111. if err != nil {
  112. return nil, makeSystemError(computeSystem, operation, "", err, events)
  113. }
  114. computeSystem.handle = handle
  115. defer func() {
  116. if err != nil {
  117. computeSystem.Close()
  118. }
  119. }()
  120. if err = computeSystem.registerCallback(ctx); err != nil {
  121. return nil, makeSystemError(computeSystem, operation, "", err, nil)
  122. }
  123. go computeSystem.waitBackground()
  124. if err = computeSystem.getCachedProperties(ctx); err != nil {
  125. return nil, err
  126. }
  127. return computeSystem, nil
  128. }
  129. func (computeSystem *System) getCachedProperties(ctx context.Context) error {
  130. props, err := computeSystem.Properties(ctx)
  131. if err != nil {
  132. return err
  133. }
  134. computeSystem.typ = strings.ToLower(props.SystemType)
  135. computeSystem.os = strings.ToLower(props.RuntimeOSType)
  136. if computeSystem.os == "" && computeSystem.typ == "container" {
  137. // Pre-RS5 HCS did not return the OS, but it only supported containers
  138. // that ran Windows.
  139. computeSystem.os = "windows"
  140. }
  141. return nil
  142. }
  143. // OS returns the operating system of the compute system, "linux" or "windows".
  144. func (computeSystem *System) OS() string {
  145. return computeSystem.os
  146. }
  147. // IsOCI returns whether processes in the compute system should be created via
  148. // OCI.
  149. func (computeSystem *System) IsOCI() bool {
  150. return computeSystem.os == "linux" && computeSystem.typ == "container"
  151. }
  152. // GetComputeSystems gets a list of the compute systems on the system that match the query
  153. func GetComputeSystems(ctx context.Context, q schema1.ComputeSystemQuery) ([]schema1.ContainerProperties, error) {
  154. operation := "hcsshim::GetComputeSystems"
  155. queryb, err := json.Marshal(q)
  156. if err != nil {
  157. return nil, err
  158. }
  159. computeSystemsJSON, resultJSON, err := vmcompute.HcsEnumerateComputeSystems(ctx, string(queryb))
  160. events := processHcsResult(ctx, resultJSON)
  161. if err != nil {
  162. return nil, &HcsError{Op: operation, Err: err, Events: events}
  163. }
  164. if computeSystemsJSON == "" {
  165. return nil, ErrUnexpectedValue
  166. }
  167. computeSystems := []schema1.ContainerProperties{}
  168. if err = json.Unmarshal([]byte(computeSystemsJSON), &computeSystems); err != nil {
  169. return nil, err
  170. }
  171. return computeSystems, nil
  172. }
  173. // Start synchronously starts the computeSystem.
  174. func (computeSystem *System) Start(ctx context.Context) (err error) {
  175. operation := "hcsshim::System::Start"
  176. // hcsStartComputeSystemContext is an async operation. Start the outer span
  177. // here to measure the full start time.
  178. ctx, span := trace.StartSpan(ctx, operation)
  179. defer span.End()
  180. defer func() { oc.SetSpanStatus(span, err) }()
  181. span.AddAttributes(trace.StringAttribute("cid", computeSystem.id))
  182. computeSystem.handleLock.RLock()
  183. defer computeSystem.handleLock.RUnlock()
  184. if computeSystem.handle == 0 {
  185. return makeSystemError(computeSystem, operation, "", ErrAlreadyClosed, nil)
  186. }
  187. // This is a very simple backoff-retry loop to limit the number
  188. // of parallel container starts if environment variable
  189. // HCSSHIM_MAX_PARALLEL_START is set to a positive integer.
  190. // It should generally only be used as a workaround to various
  191. // platform issues that exist between RS1 and RS4 as of Aug 2018
  192. if currentContainerStarts.maxParallel > 0 {
  193. for {
  194. currentContainerStarts.Lock()
  195. if currentContainerStarts.inProgress < currentContainerStarts.maxParallel {
  196. currentContainerStarts.inProgress++
  197. currentContainerStarts.Unlock()
  198. break
  199. }
  200. if currentContainerStarts.inProgress == currentContainerStarts.maxParallel {
  201. currentContainerStarts.Unlock()
  202. time.Sleep(100 * time.Millisecond)
  203. }
  204. }
  205. // Make sure we decrement the count when we are done.
  206. defer func() {
  207. currentContainerStarts.Lock()
  208. currentContainerStarts.inProgress--
  209. currentContainerStarts.Unlock()
  210. }()
  211. }
  212. resultJSON, err := vmcompute.HcsStartComputeSystem(ctx, computeSystem.handle, "")
  213. events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemStartCompleted, &timeout.SystemStart)
  214. if err != nil {
  215. return makeSystemError(computeSystem, operation, "", err, events)
  216. }
  217. return nil
  218. }
  219. // ID returns the compute system's identifier.
  220. func (computeSystem *System) ID() string {
  221. return computeSystem.id
  222. }
  223. // Shutdown requests a compute system shutdown.
  224. func (computeSystem *System) Shutdown(ctx context.Context) error {
  225. computeSystem.handleLock.RLock()
  226. defer computeSystem.handleLock.RUnlock()
  227. operation := "hcsshim::System::Shutdown"
  228. if computeSystem.handle == 0 {
  229. return nil
  230. }
  231. resultJSON, err := vmcompute.HcsShutdownComputeSystem(ctx, computeSystem.handle, "")
  232. events := processHcsResult(ctx, resultJSON)
  233. switch err {
  234. case nil, ErrVmcomputeAlreadyStopped, ErrComputeSystemDoesNotExist, ErrVmcomputeOperationPending:
  235. default:
  236. return makeSystemError(computeSystem, operation, "", err, events)
  237. }
  238. return nil
  239. }
  240. // Terminate requests a compute system terminate.
  241. func (computeSystem *System) Terminate(ctx context.Context) error {
  242. computeSystem.handleLock.RLock()
  243. defer computeSystem.handleLock.RUnlock()
  244. operation := "hcsshim::System::Terminate"
  245. if computeSystem.handle == 0 {
  246. return nil
  247. }
  248. resultJSON, err := vmcompute.HcsTerminateComputeSystem(ctx, computeSystem.handle, "")
  249. events := processHcsResult(ctx, resultJSON)
  250. switch err {
  251. case nil, ErrVmcomputeAlreadyStopped, ErrComputeSystemDoesNotExist, ErrVmcomputeOperationPending:
  252. default:
  253. return makeSystemError(computeSystem, operation, "", err, events)
  254. }
  255. return nil
  256. }
  257. // waitBackground waits for the compute system exit notification. Once received
  258. // sets `computeSystem.waitError` (if any) and unblocks all `Wait` calls.
  259. //
  260. // This MUST be called exactly once per `computeSystem.handle` but `Wait` is
  261. // safe to call multiple times.
  262. func (computeSystem *System) waitBackground() {
  263. operation := "hcsshim::System::waitBackground"
  264. ctx, span := trace.StartSpan(context.Background(), operation)
  265. defer span.End()
  266. span.AddAttributes(trace.StringAttribute("cid", computeSystem.id))
  267. err := waitForNotification(ctx, computeSystem.callbackNumber, hcsNotificationSystemExited, nil)
  268. switch err {
  269. case nil:
  270. log.G(ctx).Debug("system exited")
  271. case ErrVmcomputeUnexpectedExit:
  272. log.G(ctx).Debug("unexpected system exit")
  273. computeSystem.exitError = makeSystemError(computeSystem, operation, "", err, nil)
  274. err = nil
  275. default:
  276. err = makeSystemError(computeSystem, operation, "", err, nil)
  277. }
  278. computeSystem.closedWaitOnce.Do(func() {
  279. computeSystem.waitError = err
  280. close(computeSystem.waitBlock)
  281. })
  282. oc.SetSpanStatus(span, err)
  283. }
  284. // Wait synchronously waits for the compute system to shutdown or terminate. If
  285. // the compute system has already exited returns the previous error (if any).
  286. func (computeSystem *System) Wait() error {
  287. <-computeSystem.waitBlock
  288. return computeSystem.waitError
  289. }
  290. // ExitError returns an error describing the reason the compute system terminated.
  291. func (computeSystem *System) ExitError() error {
  292. select {
  293. case <-computeSystem.waitBlock:
  294. if computeSystem.waitError != nil {
  295. return computeSystem.waitError
  296. }
  297. return computeSystem.exitError
  298. default:
  299. return errors.New("container not exited")
  300. }
  301. }
  302. // Properties returns the requested container properties targeting a V1 schema container.
  303. func (computeSystem *System) Properties(ctx context.Context, types ...schema1.PropertyType) (*schema1.ContainerProperties, error) {
  304. computeSystem.handleLock.RLock()
  305. defer computeSystem.handleLock.RUnlock()
  306. operation := "hcsshim::System::Properties"
  307. queryBytes, err := json.Marshal(schema1.PropertyQuery{PropertyTypes: types})
  308. if err != nil {
  309. return nil, makeSystemError(computeSystem, operation, "", err, nil)
  310. }
  311. propertiesJSON, resultJSON, err := vmcompute.HcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes))
  312. events := processHcsResult(ctx, resultJSON)
  313. if err != nil {
  314. return nil, makeSystemError(computeSystem, operation, "", err, events)
  315. }
  316. if propertiesJSON == "" {
  317. return nil, ErrUnexpectedValue
  318. }
  319. properties := &schema1.ContainerProperties{}
  320. if err := json.Unmarshal([]byte(propertiesJSON), properties); err != nil {
  321. return nil, makeSystemError(computeSystem, operation, "", err, nil)
  322. }
  323. return properties, nil
  324. }
  325. // PropertiesV2 returns the requested container properties targeting a V2 schema container.
  326. func (computeSystem *System) PropertiesV2(ctx context.Context, types ...hcsschema.PropertyType) (*hcsschema.Properties, error) {
  327. computeSystem.handleLock.RLock()
  328. defer computeSystem.handleLock.RUnlock()
  329. operation := "hcsshim::System::PropertiesV2"
  330. queryBytes, err := json.Marshal(hcsschema.PropertyQuery{PropertyTypes: types})
  331. if err != nil {
  332. return nil, makeSystemError(computeSystem, operation, "", err, nil)
  333. }
  334. propertiesJSON, resultJSON, err := vmcompute.HcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes))
  335. events := processHcsResult(ctx, resultJSON)
  336. if err != nil {
  337. return nil, makeSystemError(computeSystem, operation, "", err, events)
  338. }
  339. if propertiesJSON == "" {
  340. return nil, ErrUnexpectedValue
  341. }
  342. properties := &hcsschema.Properties{}
  343. if err := json.Unmarshal([]byte(propertiesJSON), properties); err != nil {
  344. return nil, makeSystemError(computeSystem, operation, "", err, nil)
  345. }
  346. return properties, nil
  347. }
  348. // Pause pauses the execution of the computeSystem. This feature is not enabled in TP5.
  349. func (computeSystem *System) Pause(ctx context.Context) (err error) {
  350. operation := "hcsshim::System::Pause"
  351. // hcsPauseComputeSystemContext is an async peration. Start the outer span
  352. // here to measure the full pause time.
  353. ctx, span := trace.StartSpan(ctx, operation)
  354. defer span.End()
  355. defer func() { oc.SetSpanStatus(span, err) }()
  356. span.AddAttributes(trace.StringAttribute("cid", computeSystem.id))
  357. computeSystem.handleLock.RLock()
  358. defer computeSystem.handleLock.RUnlock()
  359. if computeSystem.handle == 0 {
  360. return makeSystemError(computeSystem, operation, "", ErrAlreadyClosed, nil)
  361. }
  362. resultJSON, err := vmcompute.HcsPauseComputeSystem(ctx, computeSystem.handle, "")
  363. events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemPauseCompleted, &timeout.SystemPause)
  364. if err != nil {
  365. return makeSystemError(computeSystem, operation, "", err, events)
  366. }
  367. return nil
  368. }
  369. // Resume resumes the execution of the computeSystem. This feature is not enabled in TP5.
  370. func (computeSystem *System) Resume(ctx context.Context) (err error) {
  371. operation := "hcsshim::System::Resume"
  372. // hcsResumeComputeSystemContext is an async operation. Start the outer span
  373. // here to measure the full restore time.
  374. ctx, span := trace.StartSpan(ctx, operation)
  375. defer span.End()
  376. defer func() { oc.SetSpanStatus(span, err) }()
  377. span.AddAttributes(trace.StringAttribute("cid", computeSystem.id))
  378. computeSystem.handleLock.RLock()
  379. defer computeSystem.handleLock.RUnlock()
  380. if computeSystem.handle == 0 {
  381. return makeSystemError(computeSystem, operation, "", ErrAlreadyClosed, nil)
  382. }
  383. resultJSON, err := vmcompute.HcsResumeComputeSystem(ctx, computeSystem.handle, "")
  384. events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemResumeCompleted, &timeout.SystemResume)
  385. if err != nil {
  386. return makeSystemError(computeSystem, operation, "", err, events)
  387. }
  388. return nil
  389. }
  390. func (computeSystem *System) createProcess(ctx context.Context, operation string, c interface{}) (*Process, *vmcompute.HcsProcessInformation, error) {
  391. computeSystem.handleLock.RLock()
  392. defer computeSystem.handleLock.RUnlock()
  393. if computeSystem.handle == 0 {
  394. return nil, nil, makeSystemError(computeSystem, operation, "", ErrAlreadyClosed, nil)
  395. }
  396. configurationb, err := json.Marshal(c)
  397. if err != nil {
  398. return nil, nil, makeSystemError(computeSystem, operation, "", err, nil)
  399. }
  400. configuration := string(configurationb)
  401. processInfo, processHandle, resultJSON, err := vmcompute.HcsCreateProcess(ctx, computeSystem.handle, configuration)
  402. events := processHcsResult(ctx, resultJSON)
  403. if err != nil {
  404. return nil, nil, makeSystemError(computeSystem, operation, configuration, err, events)
  405. }
  406. log.G(ctx).WithField("pid", processInfo.ProcessId).Debug("created process pid")
  407. return newProcess(processHandle, int(processInfo.ProcessId), computeSystem), &processInfo, nil
  408. }
  409. // CreateProcess launches a new process within the computeSystem.
  410. func (computeSystem *System) CreateProcess(ctx context.Context, c interface{}) (cow.Process, error) {
  411. operation := "hcsshim::System::CreateProcess"
  412. process, processInfo, err := computeSystem.createProcess(ctx, operation, c)
  413. if err != nil {
  414. return nil, err
  415. }
  416. defer func() {
  417. if err != nil {
  418. process.Close()
  419. }
  420. }()
  421. pipes, err := makeOpenFiles([]syscall.Handle{processInfo.StdInput, processInfo.StdOutput, processInfo.StdError})
  422. if err != nil {
  423. return nil, makeSystemError(computeSystem, operation, "", err, nil)
  424. }
  425. process.stdin = pipes[0]
  426. process.stdout = pipes[1]
  427. process.stderr = pipes[2]
  428. process.hasCachedStdio = true
  429. if err = process.registerCallback(ctx); err != nil {
  430. return nil, makeSystemError(computeSystem, operation, "", err, nil)
  431. }
  432. go process.waitBackground()
  433. return process, nil
  434. }
  435. // OpenProcess gets an interface to an existing process within the computeSystem.
  436. func (computeSystem *System) OpenProcess(ctx context.Context, pid int) (*Process, error) {
  437. computeSystem.handleLock.RLock()
  438. defer computeSystem.handleLock.RUnlock()
  439. operation := "hcsshim::System::OpenProcess"
  440. if computeSystem.handle == 0 {
  441. return nil, makeSystemError(computeSystem, operation, "", ErrAlreadyClosed, nil)
  442. }
  443. processHandle, resultJSON, err := vmcompute.HcsOpenProcess(ctx, computeSystem.handle, uint32(pid))
  444. events := processHcsResult(ctx, resultJSON)
  445. if err != nil {
  446. return nil, makeSystemError(computeSystem, operation, "", err, events)
  447. }
  448. process := newProcess(processHandle, pid, computeSystem)
  449. if err = process.registerCallback(ctx); err != nil {
  450. return nil, makeSystemError(computeSystem, operation, "", err, nil)
  451. }
  452. go process.waitBackground()
  453. return process, nil
  454. }
  455. // Close cleans up any state associated with the compute system but does not terminate or wait for it.
  456. func (computeSystem *System) Close() (err error) {
  457. operation := "hcsshim::System::Close"
  458. ctx, span := trace.StartSpan(context.Background(), operation)
  459. defer span.End()
  460. defer func() { oc.SetSpanStatus(span, err) }()
  461. span.AddAttributes(trace.StringAttribute("cid", computeSystem.id))
  462. computeSystem.handleLock.Lock()
  463. defer computeSystem.handleLock.Unlock()
  464. // Don't double free this
  465. if computeSystem.handle == 0 {
  466. return nil
  467. }
  468. if err = computeSystem.unregisterCallback(ctx); err != nil {
  469. return makeSystemError(computeSystem, operation, "", err, nil)
  470. }
  471. err = vmcompute.HcsCloseComputeSystem(ctx, computeSystem.handle)
  472. if err != nil {
  473. return makeSystemError(computeSystem, operation, "", err, nil)
  474. }
  475. computeSystem.handle = 0
  476. computeSystem.closedWaitOnce.Do(func() {
  477. computeSystem.waitError = ErrAlreadyClosed
  478. close(computeSystem.waitBlock)
  479. })
  480. return nil
  481. }
  482. func (computeSystem *System) registerCallback(ctx context.Context) error {
  483. callbackContext := &notifcationWatcherContext{
  484. channels: newSystemChannels(),
  485. systemID: computeSystem.id,
  486. }
  487. callbackMapLock.Lock()
  488. callbackNumber := nextCallback
  489. nextCallback++
  490. callbackMap[callbackNumber] = callbackContext
  491. callbackMapLock.Unlock()
  492. callbackHandle, err := vmcompute.HcsRegisterComputeSystemCallback(ctx, computeSystem.handle, notificationWatcherCallback, callbackNumber)
  493. if err != nil {
  494. return err
  495. }
  496. callbackContext.handle = callbackHandle
  497. computeSystem.callbackNumber = callbackNumber
  498. return nil
  499. }
  500. func (computeSystem *System) unregisterCallback(ctx context.Context) error {
  501. callbackNumber := computeSystem.callbackNumber
  502. callbackMapLock.RLock()
  503. callbackContext := callbackMap[callbackNumber]
  504. callbackMapLock.RUnlock()
  505. if callbackContext == nil {
  506. return nil
  507. }
  508. handle := callbackContext.handle
  509. if handle == 0 {
  510. return nil
  511. }
  512. // hcsUnregisterComputeSystemCallback has its own syncronization
  513. // to wait for all callbacks to complete. We must NOT hold the callbackMapLock.
  514. err := vmcompute.HcsUnregisterComputeSystemCallback(ctx, handle)
  515. if err != nil {
  516. return err
  517. }
  518. closeChannels(callbackContext.channels)
  519. callbackMapLock.Lock()
  520. delete(callbackMap, callbackNumber)
  521. callbackMapLock.Unlock()
  522. handle = 0
  523. return nil
  524. }
  525. // Modify the System by sending a request to HCS
  526. func (computeSystem *System) Modify(ctx context.Context, config interface{}) error {
  527. computeSystem.handleLock.RLock()
  528. defer computeSystem.handleLock.RUnlock()
  529. operation := "hcsshim::System::Modify"
  530. if computeSystem.handle == 0 {
  531. return makeSystemError(computeSystem, operation, "", ErrAlreadyClosed, nil)
  532. }
  533. requestBytes, err := json.Marshal(config)
  534. if err != nil {
  535. return err
  536. }
  537. requestJSON := string(requestBytes)
  538. resultJSON, err := vmcompute.HcsModifyComputeSystem(ctx, computeSystem.handle, requestJSON)
  539. events := processHcsResult(ctx, resultJSON)
  540. if err != nil {
  541. return makeSystemError(computeSystem, operation, requestJSON, err, events)
  542. }
  543. return nil
  544. }