Skip to content

Commit 00640ef

Browse files
authored
Use atomic types instead of raw values (microsoft#2241)
Per Go documentation recommendation (e.g. [link](https://pkg.go.dev/sync/atomic#AddUint64)), use the `atomic` types and their associated methods instead of the `atomic.Add*`/`.Store*` functions. This makes the intent for atomic access clearer, prevents (accidental) non-atomic access, and (for boolean variables) simplifies code. Signed-off-by: Hamza El-Saawy <[email protected]>
1 parent e2a2b5f commit 00640ef

File tree

9 files changed

+41
-57
lines changed

9 files changed

+41
-57
lines changed

internal/guest/bridge/bridge.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,8 @@ type Bridge struct {
186186
hostState *hcsv2.Host
187187

188188
quitChan chan bool
189-
// hasQuitPending when != 0 will cause no more requests to be Read.
190-
hasQuitPending uint32
189+
// hasQuitPending indicates the bridge is shutting down and cause no more requests to be Read.
190+
hasQuitPending atomic.Bool
191191

192192
protVer prot.ProtocolVersion
193193
}
@@ -243,7 +243,7 @@ func (b *Bridge) ListenAndServe(bridgeIn io.ReadCloser, bridgeOut io.WriteCloser
243243
go func() {
244244
var recverr error
245245
for {
246-
if atomic.LoadUint32(&b.hasQuitPending) == 0 {
246+
if !b.hasQuitPending.Load() {
247247
header := &prot.MessageHeader{}
248248
if err := binary.Read(bridgeIn, binary.LittleEndian, header); err != nil {
249249
if err == io.ErrUnexpectedEOF || err == os.ErrClosed { //nolint:errorlint
@@ -405,7 +405,7 @@ func (b *Bridge) ListenAndServe(bridgeIn io.ReadCloser, bridgeOut io.WriteCloser
405405
case <-b.quitChan:
406406
// The request loop needs to exit so that the teardown process begins.
407407
// Set the request loop to stop processing new messages
408-
atomic.StoreUint32(&b.hasQuitPending, 1)
408+
b.hasQuitPending.Store(true)
409409
// Wait for the request loop to process its last message. Its possible
410410
// that if it lost the race with the hasQuitPending it could be stuck in
411411
// a pending read from bridgeIn. Wait 2 seconds and kill the connection.

internal/guest/runtime/hcsv2/container.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,13 @@ type Container struct {
6161
processesMutex sync.Mutex
6262
processes map[uint32]*containerProcess
6363

64-
// Only access atomically through getStatus/setStatus.
65-
status containerStatus
64+
// current container (creation) status.
65+
// Only access through [getStatus] and [setStatus].
66+
//
67+
// Note: its more ergonomic to store the uint32 and convert to/from [containerStatus]
68+
// then use [atomic.Value] and deal with unsafe conversions to/from [any], or use [atomic.Pointer]
69+
// and deal with the extra pointer dereferencing overhead.
70+
status atomic.Uint32
6671

6772
// scratchDirPath represents the path inside the UVM where the scratch directory
6873
// of this container is located. Usually, this is either `/run/gcs/c/<containerID>` or
@@ -268,17 +273,16 @@ func (c *Container) GetStats(ctx context.Context) (*v1.Metrics, error) {
268273
return cg.Stat(cgroups.IgnoreNotExist)
269274
}
270275

271-
func (c *Container) modifyContainerConstraints(ctx context.Context, rt guestrequest.RequestType, cc *guestresource.LCOWContainerConstraints) (err error) {
276+
func (c *Container) modifyContainerConstraints(ctx context.Context, _ guestrequest.RequestType, cc *guestresource.LCOWContainerConstraints) (err error) {
272277
return c.Update(ctx, cc.Linux)
273278
}
274279

275280
func (c *Container) getStatus() containerStatus {
276-
val := atomic.LoadUint32((*uint32)(&c.status))
277-
return containerStatus(val)
281+
return containerStatus(c.status.Load())
278282
}
279283

280284
func (c *Container) setStatus(st containerStatus) {
281-
atomic.StoreUint32((*uint32)(&c.status), uint32(st))
285+
c.status.Store(uint32(st))
282286
}
283287

284288
func (c *Container) ID() string {

internal/guest/runtime/hcsv2/uvm.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,9 @@ func (h *Host) CreateContainer(ctx context.Context, id string, settings *prot.VM
308308
isSandbox: criType == "sandbox",
309309
exitType: prot.NtUnexpectedExit,
310310
processes: make(map[uint32]*containerProcess),
311-
status: containerCreating,
312311
scratchDirPath: settings.ScratchDirPath,
313312
}
313+
c.setStatus(containerCreating)
314314

315315
if err := h.AddContainer(id, c); err != nil {
316316
return nil, err

internal/jobobject/jobobject.go

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ import (
2222
// of the job and a mutex for synchronized handle access.
2323
type JobObject struct {
2424
handle windows.Handle
25-
// All accesses to this MUST be done atomically except in `Open` as the object
26-
// is being created in the function. 1 signifies that this job is currently a silo.
27-
silo uint32
25+
// silo signifies that this job is currently a silo.
26+
silo atomic.Bool
2827
mq *queue.MessageQueue
2928
handleLock sync.RWMutex
3029
}
@@ -204,9 +203,7 @@ func Open(ctx context.Context, options *Options) (_ *JobObject, err error) {
204203
handle: jobHandle,
205204
}
206205

207-
if isJobSilo(jobHandle) {
208-
job.silo = 1
209-
}
206+
job.silo.Store(isJobSilo(jobHandle))
210207

211208
// If the IOCP we'll be using to receive messages for all jobs hasn't been
212209
// created, create it and start polling.
@@ -479,7 +476,7 @@ func (job *JobObject) ApplyFileBinding(root, target string, readOnly bool) error
479476
return ErrAlreadyClosed
480477
}
481478

482-
if !job.isSilo() {
479+
if !job.silo.Load() {
483480
return ErrNotSilo
484481
}
485482

@@ -546,7 +543,7 @@ func (job *JobObject) PromoteToSilo() error {
546543
return ErrAlreadyClosed
547544
}
548545

549-
if job.isSilo() {
546+
if job.silo.Load() {
550547
return nil
551548
}
552549

@@ -569,15 +566,10 @@ func (job *JobObject) PromoteToSilo() error {
569566
return fmt.Errorf("failed to promote job to silo: %w", err)
570567
}
571568

572-
atomic.StoreUint32(&job.silo, 1)
569+
job.silo.Store(true)
573570
return nil
574571
}
575572

576-
// isSilo returns if the job object is a silo.
577-
func (job *JobObject) isSilo() bool {
578-
return atomic.LoadUint32(&job.silo) == 1
579-
}
580-
581573
// QueryPrivateWorkingSet returns the private working set size for the job. This is calculated by adding up the
582574
// private working set for every process running in the job.
583575
func (job *JobObject) QueryPrivateWorkingSet() (uint64, error) {

internal/jobobject/jobobject_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func TestSiloCreateAndOpen(t *testing.T) {
6060
}
6161
defer jobOpen.Close()
6262

63-
if !jobOpen.isSilo() {
63+
if !jobOpen.silo.Load() {
6464
t.Fatal("job is supposed to be a silo")
6565
}
6666
}

internal/log/scrub.go

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,14 @@ var (
2222
// case sensitive keywords, so "env" is not a substring on "Environment"
2323
_scrubKeywords = [][]byte{[]byte("env"), []byte("Environment")}
2424

25-
_scrub int32
25+
_scrub atomic.Bool
2626
)
2727

2828
// SetScrubbing enables scrubbing
29-
func SetScrubbing(enable bool) {
30-
v := int32(0) // cant convert from bool to int32 directly
31-
if enable {
32-
v = 1
33-
}
34-
atomic.StoreInt32(&_scrub, v)
35-
}
29+
func SetScrubbing(enable bool) { _scrub.Store(enable) }
3630

3731
// IsScrubbingEnabled checks if scrubbing is enabled
38-
func IsScrubbingEnabled() bool {
39-
v := atomic.LoadInt32(&_scrub)
40-
return v != 0
41-
}
32+
func IsScrubbingEnabled() bool { return _scrub.Load() }
4233

4334
// ScrubProcessParameters scrubs HCS Create Process requests with config parameters of
4435
// type internal/hcs/schema2.ScrubProcessParameters (aka hcsshema.ScrubProcessParameters)

internal/uvm/counter.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,15 @@
22

33
package uvm
44

5-
import (
6-
"sync/atomic"
7-
)
8-
9-
// ContainerCounter is used for where we layout things for a container in
10-
// a utility VM. For WCOW it'll be C:\c\N\. For LCOW it'll be /run/gcs/c/N/.
5+
// ContainerCounter is used for where we layout things for a container in a utility VM.
6+
// For WCOW it'll be C:\c\N\.
7+
// For LCOW it'll be /run/gcs/c/N/.
118
func (uvm *UtilityVM) ContainerCounter() uint64 {
12-
return atomic.AddUint64(&uvm.containerCounter, 1)
9+
return uvm.containerCounter.Add(1)
1310
}
1411

1512
// mountCounter is used for maintaining the number of mounts to the UVM.
1613
// This helps in generating unique mount paths for every mount.
1714
func (uvm *UtilityVM) UVMMountCounter() uint64 {
18-
return atomic.AddUint64(&uvm.mountCounter, 1)
15+
return uvm.mountCounter.Add(1)
1916
}

internal/uvm/types.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"net"
88
"sync"
9+
"sync/atomic"
910

1011
"github.com/Microsoft/go-winio/pkg/guid"
1112
"golang.org/x/sys/windows"
@@ -56,11 +57,9 @@ type UtilityVM struct {
5657
protocol uint32
5758
guestCaps schema1.GuestDefinedCapabilities
5859

59-
// containerCounter is the current number of containers that have been
60-
// created. This is never decremented in the life of the UVM.
61-
//
62-
// NOTE: All accesses to this MUST be done atomically.
63-
containerCounter uint64
60+
// containerCounter is the current number of containers that have been created.
61+
// This is never decremented in the life of the UVM.
62+
containerCounter atomic.Uint64
6463

6564
// noWritableFileShares disables mounting any writable vSMB or Plan9 shares
6665
// on the uVM. This prevents containers in the uVM modifying files and directories
@@ -118,8 +117,7 @@ type UtilityVM struct {
118117

119118
// mountCounter is the number of mounts that have been added to the UVM
120119
// This is used in generating a unique mount path inside the UVM for every mount.
121-
// Access to this variable should be done atomically.
122-
mountCounter uint64
120+
mountCounter atomic.Uint64
123121

124122
// Location that container process dumps will get written too.
125123
processDumpLocation string

test/gcs/helper_conn_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,13 @@ const (
2727
// port numbers to assign to connections.
2828
var (
2929
_pipes sync.Map
30-
_portNumber uint32 = 1
30+
_portNumber atomic.Uint32
3131
)
3232

33+
func init() {
34+
_portNumber.Store(1) // start at port 1
35+
}
36+
3337
type PipeTransport struct{}
3438

3539
var _ transport.Transport = &PipeTransport{}
@@ -250,9 +254,7 @@ func newConnectionSettings(in, out, err bool) stdio.ConnectionSettings {
250254
return c
251255
}
252256

253-
func nextPortNumber() uint32 {
254-
return atomic.AddUint32(&_portNumber, 2)
255-
}
257+
func nextPortNumber() uint32 { return _portNumber.Add(2) }
256258

257259
func TestFakeSocket(t *testing.T) {
258260
ctx := context.Background()

0 commit comments

Comments
 (0)