Skip to content

Commit 239cc8d

Browse files
committed
modified some API definitions for NetworkBlockDeviceStorageDeviceAttachment
1 parent 07aef59 commit 239cc8d

File tree

6 files changed

+92
-95
lines changed

6 files changed

+92
-95
lines changed

configuration.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ type VirtualMachineConfiguration struct {
3737
cpuCount uint
3838
memorySize uint64
3939
*pointer
40+
41+
storageDeviceConfiguration []StorageDeviceConfiguration
4042
}
4143

4244
// NewVirtualMachineConfiguration creates a new configuration.
@@ -172,20 +174,13 @@ func (v *VirtualMachineConfiguration) SetStorageDevicesVirtualMachineConfigurati
172174
}
173175
array := objc.ConvertToNSMutableArray(ptrs)
174176
C.setStorageDevicesVZVirtualMachineConfiguration(objc.Ptr(v), objc.Ptr(array))
177+
v.storageDeviceConfiguration = cs
175178
}
176179

177180
// StorageDevices return the list of storage device configuration configured in this virtual machine configuration.
178181
// Return an empty array if no storage device configuration is set.
179182
func (v *VirtualMachineConfiguration) StorageDevices() []StorageDeviceConfiguration {
180-
nsArray := objc.NewNSArray(
181-
C.storageDevicesVZVirtualMachineConfiguration(objc.Ptr(v)),
182-
)
183-
ptrs := nsArray.ToPointerSlice()
184-
storageDevices := make([]StorageDeviceConfiguration, len(ptrs))
185-
for i, ptr := range ptrs {
186-
storageDevices[i] = newVirtioBlockDeviceConfiguration(ptr)
187-
}
188-
return storageDevices
183+
return v.storageDeviceConfiguration
189184
}
190185

191186
// SetDirectorySharingDevicesVirtualMachineConfiguration sets list of directory sharing devices. Empty by default.

example/macOS/main.go

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,19 @@ func runVM(ctx context.Context) error {
7979
}()
8080

8181
// it start listening to the NBD server, if any
82-
listenNetworkBlockDevice(config)
82+
nbdAttachment := retrieveNetworkBlockDeviceStorageDeviceAttachment(config.StorageDevices())
83+
if nbdAttachment != nil {
84+
go func() {
85+
for {
86+
select {
87+
case err := <-nbdAttachment.DidEncounterError():
88+
log.Printf("NBD client has been encountered error: %v\n", err)
89+
case <-nbdAttachment.Connected():
90+
log.Println("NBD client connected with the server")
91+
}
92+
}
93+
}()
94+
}
8395

8496
// cleanup is this function is useful when finished graphic application.
8597
cleanup := func() {
@@ -105,9 +117,7 @@ func runVM(ctx context.Context) error {
105117
log.Println("finished cleanup")
106118
}
107119

108-
runtime.LockOSThread()
109120
vm.StartGraphicApplication(960, 600)
110-
runtime.UnlockOSThread()
111121

112122
cleanup()
113123

@@ -147,29 +157,30 @@ func computeMemorySize() uint64 {
147157
}
148158

149159
func createBlockDeviceConfiguration(diskPath string) (*vz.VirtioBlockDeviceConfiguration, error) {
150-
var attachment vz.StorageDeviceAttachment
151-
var err error
152-
153-
if nbdURL == "" {
154-
// create disk image with 64 GiB
155-
if err := vz.CreateDiskImage(diskPath, 64*1024*1024*1024); err != nil {
156-
if !os.IsExist(err) {
157-
return nil, fmt.Errorf("failed to create disk image: %w", err)
158-
}
160+
// create disk image with 64 GiB
161+
if err := vz.CreateDiskImage(diskPath, 64*1024*1024*1024); err != nil {
162+
if !os.IsExist(err) {
163+
return nil, fmt.Errorf("failed to create disk image: %w", err)
159164
}
165+
}
160166

161-
attachment, err = vz.NewDiskImageStorageDeviceAttachment(
162-
diskPath,
163-
false,
164-
)
165-
} else {
166-
attachment, err = vz.NewNetworkBlockDeviceStorageDeviceAttachment(
167-
nbdURL,
168-
10*time.Second,
169-
false,
170-
vz.DiskSynchronizationModeFull,
171-
)
167+
attachment, err := vz.NewDiskImageStorageDeviceAttachment(
168+
diskPath,
169+
false,
170+
)
171+
if err != nil {
172+
return nil, err
172173
}
174+
return vz.NewVirtioBlockDeviceConfiguration(attachment)
175+
}
176+
177+
func createNetworkBlockDeviceConfiguration(nbdURL string) (*vz.VirtioBlockDeviceConfiguration, error) {
178+
attachment, err := vz.NewNetworkBlockDeviceStorageDeviceAttachment(
179+
nbdURL,
180+
10*time.Second,
181+
false,
182+
vz.DiskSynchronizationModeFull,
183+
)
173184
if err != nil {
174185
return nil, err
175186
}
@@ -280,7 +291,15 @@ func setupVMConfiguration(platformConfig vz.PlatformConfiguration) (*vz.VirtualM
280291
if err != nil {
281292
return nil, fmt.Errorf("failed to create block device configuration: %w", err)
282293
}
283-
config.SetStorageDevicesVirtualMachineConfiguration([]vz.StorageDeviceConfiguration{blockDeviceConfig})
294+
sdconfigs := []vz.StorageDeviceConfiguration{blockDeviceConfig}
295+
if nbdURL != "" {
296+
ndbConfig, err := createNetworkBlockDeviceConfiguration(nbdURL)
297+
if err != nil {
298+
return nil, fmt.Errorf("failed to create network block device configuration: %w", err)
299+
}
300+
sdconfigs = append(sdconfigs, ndbConfig)
301+
}
302+
config.SetStorageDevicesVirtualMachineConfiguration(sdconfigs)
284303

285304
networkDeviceConfig, err := createNetworkDeviceConfiguration()
286305
if err != nil {
@@ -335,22 +354,11 @@ func setupVMConfiguration(platformConfig vz.PlatformConfiguration) (*vz.VirtualM
335354
return config, nil
336355
}
337356

338-
func listenNetworkBlockDevice(vm *vz.VirtualMachineConfiguration) error {
339-
storages := vm.StorageDevices()
357+
func retrieveNetworkBlockDeviceStorageDeviceAttachment(storages []vz.StorageDeviceConfiguration) *vz.NetworkBlockDeviceStorageDeviceAttachment {
340358
for _, storage := range storages {
341359
attachment := storage.Attachment()
342-
if nbdAttachment, isNbdAttachment := attachment.(*vz.NetworkBlockDeviceStorageDeviceAttachment); isNbdAttachment {
343-
nbdAttachmentStatusCh := make(chan vz.NetworkBlockDeviceStorageDeviceAttachmentStatus)
344-
nbdAttachment.Listen(nbdAttachmentStatusCh)
345-
go func() {
346-
for status := range nbdAttachmentStatusCh {
347-
if status.IsConnected() {
348-
log.Println("Successfully connected to NBD server")
349-
} else {
350-
log.Printf("Disconnected from NBD server. Error %v\n", status.Error().Error())
351-
}
352-
}
353-
}()
360+
if nbdAttachment, ok := attachment.(*vz.NetworkBlockDeviceStorageDeviceAttachment); ok {
361+
return nbdAttachment
354362
}
355363
}
356364
return nil

storage.go

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"time"
1717
"unsafe"
1818

19+
infinity "github.com/Code-Hex/go-infinity-channel"
1920
"github.com/Code-Hex/vz/v3/internal/objc"
2021
)
2122

@@ -210,12 +211,6 @@ func NewVirtioBlockDeviceConfiguration(attachment StorageDeviceAttachment) (*Vir
210211
return config, nil
211212
}
212213

213-
func newVirtioBlockDeviceConfiguration(ptr unsafe.Pointer) *VirtioBlockDeviceConfiguration {
214-
return &VirtioBlockDeviceConfiguration{
215-
pointer: objc.NewPointer(ptr),
216-
}
217-
}
218-
219214
// BlockDeviceIdentifier returns the device identifier is a string identifying the Virtio block device.
220215
// Empty string by default.
221216
//
@@ -425,17 +420,8 @@ type NetworkBlockDeviceStorageDeviceAttachment struct {
425420

426421
*baseStorageDeviceAttachment
427422

428-
attachmentStatusCh chan networkBlockDeviceStorageDeviceAttachmentStatusData
429-
}
430-
431-
type NetworkBlockDeviceStorageDeviceAttachmentStatus interface {
432-
IsConnected() bool
433-
Error() error
434-
}
435-
436-
type networkBlockDeviceStorageDeviceAttachmentStatusData struct {
437-
connected bool
438-
err error
423+
didEncounterError *infinity.Channel[error]
424+
connected *infinity.Channel[struct{}]
439425
}
440426

441427
var _ StorageDeviceAttachment = (*NetworkBlockDeviceStorageDeviceAttachment)(nil)
@@ -459,14 +445,15 @@ func NewNetworkBlockDeviceStorageDeviceAttachment(url string, timeout time.Durat
459445
return nil, err
460446
}
461447

462-
ch := make(chan networkBlockDeviceStorageDeviceAttachmentStatusData)
448+
didEncounterError := infinity.NewChannel[error]()
449+
connected := infinity.NewChannel[struct{}]()
463450

464451
handle := cgo.NewHandle(func(err error) {
465-
connected := true
466452
if err != nil {
467-
connected = false
453+
didEncounterError.In() <- err
454+
return
468455
}
469-
ch <- networkBlockDeviceStorageDeviceAttachmentStatusData{connected, err}
456+
connected.In() <- struct{}{}
470457
})
471458

472459
nserrPtr := newNSErrorAsNil()
@@ -484,7 +471,8 @@ func NewNetworkBlockDeviceStorageDeviceAttachment(url string, timeout time.Durat
484471
C.uintptr_t(handle),
485472
),
486473
),
487-
attachmentStatusCh: ch,
474+
didEncounterError: didEncounterError,
475+
connected: connected,
488476
}
489477
if err := newNSError(nserrPtr); err != nil {
490478
return nil, err
@@ -495,41 +483,48 @@ func NewNetworkBlockDeviceStorageDeviceAttachment(url string, timeout time.Durat
495483
return attachment, nil
496484
}
497485

498-
// Returns the connected value. It is true if the NBD client successfully connected to the server. False if there was an error that prevented the connection
499-
func (n *networkBlockDeviceStorageDeviceAttachmentStatusData) IsConnected() bool {
500-
return n.connected
501-
}
502-
503-
// Returns the error associated to the failed connection of the NBD client to the server
504-
func (n *networkBlockDeviceStorageDeviceAttachmentStatusData) Error() error {
505-
return n.err
486+
// Connected receive the signal via channel when the NBD client successfully connects or reconnects with the server.
487+
//
488+
// The NBD connection with the server takes place when the VM is first started, and reconnection attempts take place when the connection
489+
// times out or when the NBD client has encountered a recoverable error, such as an I/O error from the server.
490+
//
491+
// Note that the Virtualization framework may call this method multiple times during a VM’s life cycle. Reconnections are transparent to the guest.
492+
func (n *NetworkBlockDeviceStorageDeviceAttachment) Connected() <-chan struct{} {
493+
return n.connected.Out()
506494
}
507495

508-
// It allows the caller to subscribe to the attachmentStatus channel to listen to changes of the network block device attachment
509-
// This way it can be informed if the NBD client successfully connects/reconnects to the server or it encounter an error
510-
func (n *NetworkBlockDeviceStorageDeviceAttachment) Listen(statusCh chan NetworkBlockDeviceStorageDeviceAttachmentStatus) {
511-
go func() {
512-
for {
513-
status := <-n.attachmentStatusCh
514-
statusCh <- &status
515-
}
516-
}()
496+
// The DidEncounterError is triggered via the channel when the NBD client encounters an error that cannot be resolved on the client side.
497+
// In this state, the client will continue attempting to reconnect, but recovery depends entirely on the server's availability.
498+
// If the server resumes operation, the connection will recover automatically; however, until the server is restored, the client will continue to experience errors.
499+
func (n *NetworkBlockDeviceStorageDeviceAttachment) DidEncounterError() <-chan error {
500+
return n.didEncounterError.Out()
517501
}
518502

519-
//export attachmentHandler
520-
func attachmentHandler(cgoHandleUintptr C.uintptr_t, errorPtr unsafe.Pointer) {
503+
// attachmentDidEncounterErrorHandler function is called when the NBD client encounters a nonrecoverable error.
504+
// After the attachment object calls this method, the NBD client is in a nonfunctional state.
505+
//
506+
//export attachmentDidEncounterErrorHandler
507+
func attachmentDidEncounterErrorHandler(cgoHandleUintptr C.uintptr_t, errorPtr unsafe.Pointer) {
521508
cgoHandle := cgo.Handle(cgoHandleUintptr)
522509
handler := cgoHandle.Value().(func(error))
523510

524511
err := newNSError(errorPtr)
525512

526-
go handler(err)
513+
handler(err)
527514
}
528515

516+
// attachmentWasConnectedHandler function is called when a connection to the server is first established as the VM starts,
517+
// and during any reconnection attempts triggered by connection timeouts or recoverable errors encountered by the NBD client,
518+
// such as server-side I/O errors.
519+
//
520+
// Note that the Virtualization framework may invoke this method multiple times throughout the VM’s lifecycle,
521+
// ensuring reconnection processes remain seamless and transparent to the guest.
522+
// For more details, see: https://developer.apple.com/documentation/virtualization/vznetworkblockdevicestoragedeviceattachmentdelegate/4168511-attachmentwasconnected?language=objc
523+
//
529524
//export attachmentWasConnectedHandler
530525
func attachmentWasConnectedHandler(cgoHandleUintptr C.uintptr_t) {
531526
cgoHandle := cgo.Handle(cgoHandleUintptr)
532527
handler := cgoHandle.Value().(func(error))
533528

534-
go handler(nil)
529+
handler(nil)
535530
}

virtualization_14.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#import <Virtualization/Virtualization.h>
1515

1616
/* exported from cgo */
17-
void attachmentHandler(uintptr_t cgoHandle, void *err);
17+
void attachmentDidEncounterErrorHandler(uintptr_t cgoHandle, void *err);
1818
void attachmentWasConnectedHandler(uintptr_t cgoHandle);
1919

2020
/* macOS 14 API */

virtualization_14.m

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,11 @@ - (instancetype)initWithHandle:(uintptr_t)cgoHandle
104104

105105
- (void)attachment:(VZNetworkBlockDeviceStorageDeviceAttachment *)attachment didEncounterError:(NSError *)error
106106
{
107-
attachmentHandler(_cgoHandle, error);
107+
attachmentDidEncounterErrorHandler(_cgoHandle, error);
108108
}
109109

110110
- (void)attachmentWasConnected:(VZNetworkBlockDeviceStorageDeviceAttachment *)attachment
111111
{
112112
attachmentWasConnectedHandler(_cgoHandle);
113113
}
114114
@end
115-

virtualization_15.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ bool isNestedVirtualizationSupported()
1111
{
1212
#ifdef INCLUDE_TARGET_OSX_15
1313
if (@available(macOS 15, *)) {
14-
return (bool) VZGenericPlatformConfiguration.isNestedVirtualizationSupported;
14+
return (bool)VZGenericPlatformConfiguration.isNestedVirtualizationSupported;
1515
}
1616
#endif
1717
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
@@ -25,7 +25,7 @@ void setNestedVirtualizationEnabled(void *config, bool nestedVirtualizationEnabl
2525
#ifdef INCLUDE_TARGET_OSX_15
2626
if (@available(macOS 15, *)) {
2727
VZGenericPlatformConfiguration *platformConfig = (VZGenericPlatformConfiguration *)config;
28-
platformConfig.nestedVirtualizationEnabled = (BOOL) nestedVirtualizationEnabled;
28+
platformConfig.nestedVirtualizationEnabled = (BOOL)nestedVirtualizationEnabled;
2929
return;
3030
}
3131
#endif

0 commit comments

Comments
 (0)