system.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. package hcs
  2. import (
  3. "encoding/json"
  4. "os"
  5. "strconv"
  6. "sync"
  7. "syscall"
  8. "time"
  9. "github.com/Microsoft/hcsshim/internal/interop"
  10. "github.com/Microsoft/hcsshim/internal/logfields"
  11. "github.com/Microsoft/hcsshim/internal/schema1"
  12. "github.com/Microsoft/hcsshim/internal/timeout"
  13. "github.com/sirupsen/logrus"
  14. )
  15. // currentContainerStarts is used to limit the number of concurrent container
  16. // starts.
  17. var currentContainerStarts containerStarts
  18. type containerStarts struct {
  19. maxParallel int
  20. inProgress int
  21. sync.Mutex
  22. }
  23. func init() {
  24. mpsS := os.Getenv("HCSSHIM_MAX_PARALLEL_START")
  25. if len(mpsS) > 0 {
  26. mpsI, err := strconv.Atoi(mpsS)
  27. if err != nil || mpsI < 0 {
  28. return
  29. }
  30. currentContainerStarts.maxParallel = mpsI
  31. }
  32. }
  33. type System struct {
  34. handleLock sync.RWMutex
  35. handle hcsSystem
  36. id string
  37. callbackNumber uintptr
  38. logctx logrus.Fields
  39. closedWaitOnce sync.Once
  40. waitBlock chan struct{}
  41. waitError error
  42. }
  43. func newSystem(id string) *System {
  44. return &System{
  45. id: id,
  46. logctx: logrus.Fields{
  47. logfields.ContainerID: id,
  48. },
  49. waitBlock: make(chan struct{}),
  50. }
  51. }
  52. func (computeSystem *System) logOperationBegin(operation string) {
  53. logOperationBegin(
  54. computeSystem.logctx,
  55. operation+" - Begin Operation")
  56. }
  57. func (computeSystem *System) logOperationEnd(operation string, err error) {
  58. var result string
  59. if err == nil {
  60. result = "Success"
  61. } else {
  62. result = "Error"
  63. }
  64. logOperationEnd(
  65. computeSystem.logctx,
  66. operation+" - End Operation - "+result,
  67. err)
  68. }
  69. // CreateComputeSystem creates a new compute system with the given configuration but does not start it.
  70. func CreateComputeSystem(id string, hcsDocumentInterface interface{}) (_ *System, err error) {
  71. operation := "hcsshim::CreateComputeSystem"
  72. computeSystem := newSystem(id)
  73. computeSystem.logOperationBegin(operation)
  74. defer func() { computeSystem.logOperationEnd(operation, err) }()
  75. hcsDocumentB, err := json.Marshal(hcsDocumentInterface)
  76. if err != nil {
  77. return nil, err
  78. }
  79. hcsDocument := string(hcsDocumentB)
  80. logrus.WithFields(computeSystem.logctx).
  81. WithField(logfields.JSON, hcsDocument).
  82. Debug("HCS ComputeSystem Document")
  83. var (
  84. resultp *uint16
  85. identity syscall.Handle
  86. createError error
  87. )
  88. syscallWatcher(computeSystem.logctx, func() {
  89. createError = hcsCreateComputeSystem(id, hcsDocument, identity, &computeSystem.handle, &resultp)
  90. })
  91. if createError == nil || IsPending(createError) {
  92. if err = computeSystem.registerCallback(); err != nil {
  93. // Terminate the compute system if it still exists. We're okay to
  94. // ignore a failure here.
  95. computeSystem.Terminate()
  96. return nil, makeSystemError(computeSystem, operation, "", err, nil)
  97. }
  98. }
  99. events, err := processAsyncHcsResult(createError, resultp, computeSystem.callbackNumber, hcsNotificationSystemCreateCompleted, &timeout.SystemCreate)
  100. if err != nil {
  101. if err == ErrTimeout {
  102. // Terminate the compute system if it still exists. We're okay to
  103. // ignore a failure here.
  104. computeSystem.Terminate()
  105. }
  106. return nil, makeSystemError(computeSystem, operation, hcsDocument, err, events)
  107. }
  108. go computeSystem.waitBackground()
  109. return computeSystem, nil
  110. }
  111. // OpenComputeSystem opens an existing compute system by ID.
  112. func OpenComputeSystem(id string) (_ *System, err error) {
  113. operation := "hcsshim::OpenComputeSystem"
  114. computeSystem := newSystem(id)
  115. computeSystem.logOperationBegin(operation)
  116. defer func() {
  117. if IsNotExist(err) {
  118. computeSystem.logOperationEnd(operation, nil)
  119. } else {
  120. computeSystem.logOperationEnd(operation, err)
  121. }
  122. }()
  123. var (
  124. handle hcsSystem
  125. resultp *uint16
  126. )
  127. err = hcsOpenComputeSystem(id, &handle, &resultp)
  128. events := processHcsResult(resultp)
  129. if err != nil {
  130. return nil, makeSystemError(computeSystem, operation, "", err, events)
  131. }
  132. computeSystem.handle = handle
  133. if err = computeSystem.registerCallback(); err != nil {
  134. return nil, makeSystemError(computeSystem, operation, "", err, nil)
  135. }
  136. go computeSystem.waitBackground()
  137. return computeSystem, nil
  138. }
  139. // GetComputeSystems gets a list of the compute systems on the system that match the query
  140. func GetComputeSystems(q schema1.ComputeSystemQuery) (_ []schema1.ContainerProperties, err error) {
  141. operation := "hcsshim::GetComputeSystems"
  142. fields := logrus.Fields{}
  143. logOperationBegin(
  144. fields,
  145. operation+" - Begin Operation")
  146. defer func() {
  147. var result string
  148. if err == nil {
  149. result = "Success"
  150. } else {
  151. result = "Error"
  152. }
  153. logOperationEnd(
  154. fields,
  155. operation+" - End Operation - "+result,
  156. err)
  157. }()
  158. queryb, err := json.Marshal(q)
  159. if err != nil {
  160. return nil, err
  161. }
  162. query := string(queryb)
  163. logrus.WithFields(fields).
  164. WithField(logfields.JSON, query).
  165. Debug("HCS ComputeSystem Query")
  166. var (
  167. resultp *uint16
  168. computeSystemsp *uint16
  169. )
  170. syscallWatcher(fields, func() {
  171. err = hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp)
  172. })
  173. events := processHcsResult(resultp)
  174. if err != nil {
  175. return nil, &HcsError{Op: operation, Err: err, Events: events}
  176. }
  177. if computeSystemsp == nil {
  178. return nil, ErrUnexpectedValue
  179. }
  180. computeSystemsRaw := interop.ConvertAndFreeCoTaskMemBytes(computeSystemsp)
  181. computeSystems := []schema1.ContainerProperties{}
  182. if err = json.Unmarshal(computeSystemsRaw, &computeSystems); err != nil {
  183. return nil, err
  184. }
  185. return computeSystems, nil
  186. }
  187. // Start synchronously starts the computeSystem.
  188. func (computeSystem *System) Start() (err error) {
  189. computeSystem.handleLock.RLock()
  190. defer computeSystem.handleLock.RUnlock()
  191. operation := "hcsshim::ComputeSystem::Start"
  192. computeSystem.logOperationBegin(operation)
  193. defer func() { computeSystem.logOperationEnd(operation, err) }()
  194. if computeSystem.handle == 0 {
  195. return makeSystemError(computeSystem, "Start", "", ErrAlreadyClosed, nil)
  196. }
  197. // This is a very simple backoff-retry loop to limit the number
  198. // of parallel container starts if environment variable
  199. // HCSSHIM_MAX_PARALLEL_START is set to a positive integer.
  200. // It should generally only be used as a workaround to various
  201. // platform issues that exist between RS1 and RS4 as of Aug 2018
  202. if currentContainerStarts.maxParallel > 0 {
  203. for {
  204. currentContainerStarts.Lock()
  205. if currentContainerStarts.inProgress < currentContainerStarts.maxParallel {
  206. currentContainerStarts.inProgress++
  207. currentContainerStarts.Unlock()
  208. break
  209. }
  210. if currentContainerStarts.inProgress == currentContainerStarts.maxParallel {
  211. currentContainerStarts.Unlock()
  212. time.Sleep(100 * time.Millisecond)
  213. }
  214. }
  215. // Make sure we decrement the count when we are done.
  216. defer func() {
  217. currentContainerStarts.Lock()
  218. currentContainerStarts.inProgress--
  219. currentContainerStarts.Unlock()
  220. }()
  221. }
  222. var resultp *uint16
  223. syscallWatcher(computeSystem.logctx, func() {
  224. err = hcsStartComputeSystem(computeSystem.handle, "", &resultp)
  225. })
  226. events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemStartCompleted, &timeout.SystemStart)
  227. if err != nil {
  228. return makeSystemError(computeSystem, "Start", "", err, events)
  229. }
  230. return nil
  231. }
  232. // ID returns the compute system's identifier.
  233. func (computeSystem *System) ID() string {
  234. return computeSystem.id
  235. }
  236. // Shutdown requests a compute system shutdown, if IsPending() on the error returned is true,
  237. // it may not actually be shut down until Wait() succeeds.
  238. func (computeSystem *System) Shutdown() (err error) {
  239. computeSystem.handleLock.RLock()
  240. defer computeSystem.handleLock.RUnlock()
  241. operation := "hcsshim::ComputeSystem::Shutdown"
  242. computeSystem.logOperationBegin(operation)
  243. defer func() {
  244. if IsAlreadyClosed(err) || IsAlreadyStopped(err) || IsPending(err) {
  245. computeSystem.logOperationEnd(operation, nil)
  246. } else {
  247. computeSystem.logOperationEnd(operation, err)
  248. }
  249. }()
  250. if computeSystem.handle == 0 {
  251. return makeSystemError(computeSystem, "Shutdown", "", ErrAlreadyClosed, nil)
  252. }
  253. var resultp *uint16
  254. syscallWatcher(computeSystem.logctx, func() {
  255. err = hcsShutdownComputeSystem(computeSystem.handle, "", &resultp)
  256. })
  257. events := processHcsResult(resultp)
  258. if err != nil {
  259. return makeSystemError(computeSystem, "Shutdown", "", err, events)
  260. }
  261. return nil
  262. }
  263. // Terminate requests a compute system terminate, if IsPending() on the error returned is true,
  264. // it may not actually be shut down until Wait() succeeds.
  265. func (computeSystem *System) Terminate() (err error) {
  266. computeSystem.handleLock.RLock()
  267. defer computeSystem.handleLock.RUnlock()
  268. operation := "hcsshim::ComputeSystem::Terminate"
  269. computeSystem.logOperationBegin(operation)
  270. defer func() {
  271. if IsAlreadyClosed(err) || IsAlreadyStopped(err) || IsPending(err) {
  272. computeSystem.logOperationEnd(operation, nil)
  273. } else {
  274. computeSystem.logOperationEnd(operation, err)
  275. }
  276. }()
  277. if computeSystem.handle == 0 {
  278. return makeSystemError(computeSystem, "Terminate", "", ErrAlreadyClosed, nil)
  279. }
  280. var resultp *uint16
  281. syscallWatcher(computeSystem.logctx, func() {
  282. err = hcsTerminateComputeSystem(computeSystem.handle, "", &resultp)
  283. })
  284. events := processHcsResult(resultp)
  285. if err != nil && err != ErrVmcomputeAlreadyStopped {
  286. return makeSystemError(computeSystem, "Terminate", "", err, events)
  287. }
  288. return nil
  289. }
  290. // waitBackground waits for the compute system exit notification. Once received
  291. // sets `computeSystem.waitError` (if any) and unblocks all `Wait`,
  292. // `WaitExpectedError`, and `WaitTimeout` calls.
  293. //
  294. // This MUST be called exactly once per `computeSystem.handle` but `Wait`,
  295. // `WaitExpectedError`, and `WaitTimeout` are safe to call multiple times.
  296. func (computeSystem *System) waitBackground() {
  297. computeSystem.waitError = waitForNotification(computeSystem.callbackNumber, hcsNotificationSystemExited, nil)
  298. computeSystem.closedWaitOnce.Do(func() {
  299. close(computeSystem.waitBlock)
  300. })
  301. }
  302. // Wait synchronously waits for the compute system to shutdown or terminate. If
  303. // the compute system has already exited returns the previous error (if any).
  304. func (computeSystem *System) Wait() (err error) {
  305. operation := "hcsshim::ComputeSystem::Wait"
  306. computeSystem.logOperationBegin(operation)
  307. defer func() { computeSystem.logOperationEnd(operation, err) }()
  308. <-computeSystem.waitBlock
  309. if computeSystem.waitError != nil {
  310. return makeSystemError(computeSystem, "Wait", "", computeSystem.waitError, nil)
  311. }
  312. return nil
  313. }
  314. // WaitExpectedError synchronously waits for the compute system to shutdown or
  315. // terminate and returns the error (if any) as long as it does not match
  316. // `expected`. If the compute system has already exited returns the previous
  317. // error (if any) as long as it does not match `expected`.
  318. func (computeSystem *System) WaitExpectedError(expected error) (err error) {
  319. operation := "hcsshim::ComputeSystem::WaitExpectedError"
  320. computeSystem.logOperationBegin(operation)
  321. defer func() { computeSystem.logOperationEnd(operation, err) }()
  322. <-computeSystem.waitBlock
  323. if computeSystem.waitError != nil && getInnerError(computeSystem.waitError) != expected {
  324. return makeSystemError(computeSystem, "WaitExpectedError", "", computeSystem.waitError, nil)
  325. }
  326. return nil
  327. }
  328. // WaitTimeout synchronously waits for the compute system to terminate or the
  329. // duration to elapse. If the timeout expires, `IsTimeout(err) == true`. If
  330. // the compute system has already exited returns the previous error (if any).
  331. func (computeSystem *System) WaitTimeout(timeout time.Duration) (err error) {
  332. operation := "hcsshim::ComputeSystem::WaitTimeout"
  333. computeSystem.logOperationBegin(operation)
  334. defer func() { computeSystem.logOperationEnd(operation, err) }()
  335. select {
  336. case <-computeSystem.waitBlock:
  337. if computeSystem.waitError != nil {
  338. return makeSystemError(computeSystem, "WaitTimeout", "", computeSystem.waitError, nil)
  339. }
  340. return nil
  341. case <-time.After(timeout):
  342. return makeSystemError(computeSystem, "WaitTimeout", "", ErrTimeout, nil)
  343. }
  344. }
  345. func (computeSystem *System) Properties(types ...schema1.PropertyType) (_ *schema1.ContainerProperties, err error) {
  346. computeSystem.handleLock.RLock()
  347. defer computeSystem.handleLock.RUnlock()
  348. operation := "hcsshim::ComputeSystem::Properties"
  349. computeSystem.logOperationBegin(operation)
  350. defer func() { computeSystem.logOperationEnd(operation, err) }()
  351. queryBytes, err := json.Marshal(schema1.PropertyQuery{PropertyTypes: types})
  352. if err != nil {
  353. return nil, makeSystemError(computeSystem, "Properties", "", err, nil)
  354. }
  355. queryString := string(queryBytes)
  356. logrus.WithFields(computeSystem.logctx).
  357. WithField(logfields.JSON, queryString).
  358. Debug("HCS ComputeSystem Properties Query")
  359. var resultp, propertiesp *uint16
  360. syscallWatcher(computeSystem.logctx, func() {
  361. err = hcsGetComputeSystemProperties(computeSystem.handle, string(queryString), &propertiesp, &resultp)
  362. })
  363. events := processHcsResult(resultp)
  364. if err != nil {
  365. return nil, makeSystemError(computeSystem, "Properties", "", err, events)
  366. }
  367. if propertiesp == nil {
  368. return nil, ErrUnexpectedValue
  369. }
  370. propertiesRaw := interop.ConvertAndFreeCoTaskMemBytes(propertiesp)
  371. properties := &schema1.ContainerProperties{}
  372. if err := json.Unmarshal(propertiesRaw, properties); err != nil {
  373. return nil, makeSystemError(computeSystem, "Properties", "", err, nil)
  374. }
  375. return properties, nil
  376. }
  377. // Pause pauses the execution of the computeSystem. This feature is not enabled in TP5.
  378. func (computeSystem *System) Pause() (err error) {
  379. computeSystem.handleLock.RLock()
  380. defer computeSystem.handleLock.RUnlock()
  381. operation := "hcsshim::ComputeSystem::Pause"
  382. computeSystem.logOperationBegin(operation)
  383. defer func() { computeSystem.logOperationEnd(operation, err) }()
  384. if computeSystem.handle == 0 {
  385. return makeSystemError(computeSystem, "Pause", "", ErrAlreadyClosed, nil)
  386. }
  387. var resultp *uint16
  388. syscallWatcher(computeSystem.logctx, func() {
  389. err = hcsPauseComputeSystem(computeSystem.handle, "", &resultp)
  390. })
  391. events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemPauseCompleted, &timeout.SystemPause)
  392. if err != nil {
  393. return makeSystemError(computeSystem, "Pause", "", err, events)
  394. }
  395. return nil
  396. }
  397. // Resume resumes the execution of the computeSystem. This feature is not enabled in TP5.
  398. func (computeSystem *System) Resume() (err error) {
  399. computeSystem.handleLock.RLock()
  400. defer computeSystem.handleLock.RUnlock()
  401. operation := "hcsshim::ComputeSystem::Resume"
  402. computeSystem.logOperationBegin(operation)
  403. defer func() { computeSystem.logOperationEnd(operation, err) }()
  404. if computeSystem.handle == 0 {
  405. return makeSystemError(computeSystem, "Resume", "", ErrAlreadyClosed, nil)
  406. }
  407. var resultp *uint16
  408. syscallWatcher(computeSystem.logctx, func() {
  409. err = hcsResumeComputeSystem(computeSystem.handle, "", &resultp)
  410. })
  411. events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemResumeCompleted, &timeout.SystemResume)
  412. if err != nil {
  413. return makeSystemError(computeSystem, "Resume", "", err, events)
  414. }
  415. return nil
  416. }
  417. // CreateProcess launches a new process within the computeSystem.
  418. func (computeSystem *System) CreateProcess(c interface{}) (_ *Process, err error) {
  419. computeSystem.handleLock.RLock()
  420. defer computeSystem.handleLock.RUnlock()
  421. operation := "hcsshim::ComputeSystem::CreateProcess"
  422. computeSystem.logOperationBegin(operation)
  423. defer func() { computeSystem.logOperationEnd(operation, err) }()
  424. var (
  425. processInfo hcsProcessInformation
  426. processHandle hcsProcess
  427. resultp *uint16
  428. )
  429. if computeSystem.handle == 0 {
  430. return nil, makeSystemError(computeSystem, "CreateProcess", "", ErrAlreadyClosed, nil)
  431. }
  432. configurationb, err := json.Marshal(c)
  433. if err != nil {
  434. return nil, makeSystemError(computeSystem, "CreateProcess", "", err, nil)
  435. }
  436. configuration := string(configurationb)
  437. logrus.WithFields(computeSystem.logctx).
  438. WithField(logfields.JSON, configuration).
  439. Debug("HCS ComputeSystem Process Document")
  440. syscallWatcher(computeSystem.logctx, func() {
  441. err = hcsCreateProcess(computeSystem.handle, configuration, &processInfo, &processHandle, &resultp)
  442. })
  443. events := processHcsResult(resultp)
  444. if err != nil {
  445. return nil, makeSystemError(computeSystem, "CreateProcess", configuration, err, events)
  446. }
  447. logrus.WithFields(computeSystem.logctx).
  448. WithField(logfields.ProcessID, processInfo.ProcessId).
  449. Debug("HCS ComputeSystem CreateProcess PID")
  450. process := newProcess(processHandle, int(processInfo.ProcessId), computeSystem)
  451. process.cachedPipes = &cachedPipes{
  452. stdIn: processInfo.StdInput,
  453. stdOut: processInfo.StdOutput,
  454. stdErr: processInfo.StdError,
  455. }
  456. if err = process.registerCallback(); err != nil {
  457. return nil, makeSystemError(computeSystem, "CreateProcess", "", err, nil)
  458. }
  459. go process.waitBackground()
  460. return process, nil
  461. }
  462. // OpenProcess gets an interface to an existing process within the computeSystem.
  463. func (computeSystem *System) OpenProcess(pid int) (_ *Process, err error) {
  464. computeSystem.handleLock.RLock()
  465. defer computeSystem.handleLock.RUnlock()
  466. // Add PID for the context of this operation
  467. computeSystem.logctx[logfields.ProcessID] = pid
  468. defer delete(computeSystem.logctx, logfields.ProcessID)
  469. operation := "hcsshim::ComputeSystem::OpenProcess"
  470. computeSystem.logOperationBegin(operation)
  471. defer func() { computeSystem.logOperationEnd(operation, err) }()
  472. var (
  473. processHandle hcsProcess
  474. resultp *uint16
  475. )
  476. if computeSystem.handle == 0 {
  477. return nil, makeSystemError(computeSystem, "OpenProcess", "", ErrAlreadyClosed, nil)
  478. }
  479. syscallWatcher(computeSystem.logctx, func() {
  480. err = hcsOpenProcess(computeSystem.handle, uint32(pid), &processHandle, &resultp)
  481. })
  482. events := processHcsResult(resultp)
  483. if err != nil {
  484. return nil, makeSystemError(computeSystem, "OpenProcess", "", err, events)
  485. }
  486. process := newProcess(processHandle, pid, computeSystem)
  487. if err = process.registerCallback(); err != nil {
  488. return nil, makeSystemError(computeSystem, "OpenProcess", "", err, nil)
  489. }
  490. go process.waitBackground()
  491. return process, nil
  492. }
  493. // Close cleans up any state associated with the compute system but does not terminate or wait for it.
  494. func (computeSystem *System) Close() (err error) {
  495. computeSystem.handleLock.Lock()
  496. defer computeSystem.handleLock.Unlock()
  497. operation := "hcsshim::ComputeSystem::Close"
  498. computeSystem.logOperationBegin(operation)
  499. defer func() { computeSystem.logOperationEnd(operation, err) }()
  500. // Don't double free this
  501. if computeSystem.handle == 0 {
  502. return nil
  503. }
  504. if err = computeSystem.unregisterCallback(); err != nil {
  505. return makeSystemError(computeSystem, "Close", "", err, nil)
  506. }
  507. syscallWatcher(computeSystem.logctx, func() {
  508. err = hcsCloseComputeSystem(computeSystem.handle)
  509. })
  510. if err != nil {
  511. return makeSystemError(computeSystem, "Close", "", err, nil)
  512. }
  513. computeSystem.handle = 0
  514. computeSystem.closedWaitOnce.Do(func() {
  515. close(computeSystem.waitBlock)
  516. })
  517. return nil
  518. }
  519. func (computeSystem *System) registerCallback() error {
  520. context := &notifcationWatcherContext{
  521. channels: newSystemChannels(),
  522. systemID: computeSystem.id,
  523. }
  524. callbackMapLock.Lock()
  525. callbackNumber := nextCallback
  526. nextCallback++
  527. callbackMap[callbackNumber] = context
  528. callbackMapLock.Unlock()
  529. var callbackHandle hcsCallback
  530. err := hcsRegisterComputeSystemCallback(computeSystem.handle, notificationWatcherCallback, callbackNumber, &callbackHandle)
  531. if err != nil {
  532. return err
  533. }
  534. context.handle = callbackHandle
  535. computeSystem.callbackNumber = callbackNumber
  536. return nil
  537. }
  538. func (computeSystem *System) unregisterCallback() error {
  539. callbackNumber := computeSystem.callbackNumber
  540. callbackMapLock.RLock()
  541. context := callbackMap[callbackNumber]
  542. callbackMapLock.RUnlock()
  543. if context == nil {
  544. return nil
  545. }
  546. handle := context.handle
  547. if handle == 0 {
  548. return nil
  549. }
  550. // hcsUnregisterComputeSystemCallback has its own syncronization
  551. // to wait for all callbacks to complete. We must NOT hold the callbackMapLock.
  552. err := hcsUnregisterComputeSystemCallback(handle)
  553. if err != nil {
  554. return err
  555. }
  556. closeChannels(context.channels)
  557. callbackMapLock.Lock()
  558. delete(callbackMap, callbackNumber)
  559. callbackMapLock.Unlock()
  560. handle = 0
  561. return nil
  562. }
  563. // Modify the System by sending a request to HCS
  564. func (computeSystem *System) Modify(config interface{}) (err error) {
  565. computeSystem.handleLock.RLock()
  566. defer computeSystem.handleLock.RUnlock()
  567. operation := "hcsshim::ComputeSystem::Modify"
  568. computeSystem.logOperationBegin(operation)
  569. defer func() { computeSystem.logOperationEnd(operation, err) }()
  570. if computeSystem.handle == 0 {
  571. return makeSystemError(computeSystem, "Modify", "", ErrAlreadyClosed, nil)
  572. }
  573. requestJSON, err := json.Marshal(config)
  574. if err != nil {
  575. return err
  576. }
  577. requestString := string(requestJSON)
  578. logrus.WithFields(computeSystem.logctx).
  579. WithField(logfields.JSON, requestString).
  580. Debug("HCS ComputeSystem Modify Document")
  581. var resultp *uint16
  582. syscallWatcher(computeSystem.logctx, func() {
  583. err = hcsModifyComputeSystem(computeSystem.handle, requestString, &resultp)
  584. })
  585. events := processHcsResult(resultp)
  586. if err != nil {
  587. return makeSystemError(computeSystem, "Modify", requestString, err, events)
  588. }
  589. return nil
  590. }