Introduce StatsAPI and it's initial implementation
[govpp.git] / adapter / vppapiclient / vppapiclient.go
similarity index 56%
rename from adapter/vppapiclient/vppapiclient_adapter.go
rename to adapter/vppapiclient/vppapiclient.go
index e62bccd..34ad199 100644 (file)
@@ -14,8 +14,6 @@
 
 // +build !windows,!darwin
 
-// Package vppapiclient is the default VPP adapter being used for the connection with VPP via shared memory.
-// It is based on the communication with the vppapiclient VPP library written in C via CGO.
 package vppapiclient
 
 /*
@@ -41,7 +39,7 @@ typedef struct __attribute__((__packed__)) _reply_header {
 } reply_header_t;
 
 static void
-govpp_msg_callback (unsigned char *data, int size)
+govpp_msg_callback(unsigned char *data, int size)
 {
     reply_header_t *header = ((reply_header_t *)data);
     go_msg_callback(ntohs(header->msg_id), data, size);
@@ -56,13 +54,13 @@ govpp_send(uint32_t context, void *data, size_t size)
 }
 
 static int
-govpp_connect (char *shm)
+govpp_connect(char *shm)
 {
     return vac_connect("govpp", shm, govpp_msg_callback, 32);
 }
 
 static int
-govvp_disconnect()
+govpp_disconnect()
 {
     return vac_disconnect();
 }
@@ -87,32 +85,34 @@ import (
 )
 
 const (
-       // watchedFolder is a folder where vpp's shared memory is supposed to be created.
-       // File system events are monitored in this folder.
-       watchedFolder = "/dev/shm/"
-       // watchedFile is a default name of the file in the watchedFolder. Once the file is present,
-       // the vpp is ready to accept a new connection.
-       watchedFile = "vpe-api"
+       // shmDir is a directory where shared memory is supposed to be created.
+       shmDir = "/dev/shm/"
+       // vppShmFile is a default name of the file in the shmDir.
+       vppShmFile = "vpe-api"
 )
 
-// vppAPIClientAdapter is the opaque context of the adapter.
-type vppAPIClientAdapter struct {
-       shmPrefix string
-       callback  adapter.MsgCallback
-}
+// global VPP binary API client adapter context
+var client *VppClient
 
-var vppClient *vppAPIClientAdapter // global vpp API client adapter context
+// VppClient is the default implementation of the VppAPI.
+type VppClient struct {
+       shmPrefix   string
+       msgCallback adapter.MsgCallback
+}
 
-// NewVppAdapter returns a new vpp API client adapter.
-func NewVppAdapter(shmPrefix string) adapter.VppAdapter {
-       return &vppAPIClientAdapter{
+// NewVppClient returns a new VPP binary API client.
+func NewVppClient(shmPrefix string) *VppClient {
+       return &VppClient{
                shmPrefix: shmPrefix,
        }
 }
 
 // Connect connects the process to VPP.
-func (a *vppAPIClientAdapter) Connect() error {
-       vppClient = a
+func (a *VppClient) Connect() error {
+       if client != nil {
+               return fmt.Errorf("already connected to binary API, disconnect first")
+       }
+
        var rc _Ctype_int
        if a.shmPrefix == "" {
                rc = C.govpp_connect(nil)
@@ -121,18 +121,27 @@ func (a *vppAPIClientAdapter) Connect() error {
                rc = C.govpp_connect(shm)
        }
        if rc != 0 {
-               return fmt.Errorf("unable to connect to VPP (error=%d)", rc)
+               return fmt.Errorf("connecting to VPP binary API failed (rc=%v)", rc)
        }
+
+       client = a
        return nil
 }
 
 // Disconnect disconnects the process from VPP.
-func (a *vppAPIClientAdapter) Disconnect() {
-       C.govvp_disconnect()
+func (a *VppClient) Disconnect() error {
+       client = nil
+
+       rc := C.govpp_disconnect()
+       if rc != 0 {
+               return fmt.Errorf("disconnecting from VPP binary API failed (rc=%v)", rc)
+       }
+
+       return nil
 }
 
 // GetMsgID returns a runtime message ID for the given message name and CRC.
-func (a *vppAPIClientAdapter) GetMsgID(msgName string, msgCrc string) (uint16, error) {
+func (a *VppClient) GetMsgID(msgName string, msgCrc string) (uint16, error) {
        nameAndCrc := C.CString(msgName + "_" + msgCrc)
        defer C.free(unsafe.Pointer(nameAndCrc))
 
@@ -146,45 +155,56 @@ func (a *vppAPIClientAdapter) GetMsgID(msgName string, msgCrc string) (uint16, e
 }
 
 // SendMsg sends a binary-encoded message to VPP.
-func (a *vppAPIClientAdapter) SendMsg(context uint32, data []byte) error {
+func (a *VppClient) SendMsg(context uint32, data []byte) error {
        rc := C.govpp_send(C.uint32_t(context), unsafe.Pointer(&data[0]), C.size_t(len(data)))
        if rc != 0 {
-               return fmt.Errorf("unable to send the message (error=%d)", rc)
+               return fmt.Errorf("unable to send the message (rc=%v)", rc)
        }
        return nil
 }
 
-// SetMsgCallback sets a callback function that will be called by the adapter whenever a message comes from VPP.
-func (a *vppAPIClientAdapter) SetMsgCallback(cb adapter.MsgCallback) {
-       a.callback = cb
+// SetMsgCallback sets a callback function that will be called by the adapter
+// whenever a message comes from VPP.
+func (a *VppClient) SetMsgCallback(cb adapter.MsgCallback) {
+       a.msgCallback = cb
 }
 
 // WaitReady blocks until shared memory for sending
 // binary api calls is present on the file system.
-func (a *vppAPIClientAdapter) WaitReady() error {
-       // Path to the shared memory segment
+func (a *VppClient) WaitReady() error {
        var path string
+
+       // join the path to the shared memory segment
        if a.shmPrefix == "" {
-               path = filepath.Join(watchedFolder, watchedFile)
+               path = filepath.Join(shmDir, vppShmFile)
        } else {
-               path = filepath.Join(watchedFolder, a.shmPrefix+"-"+watchedFile)
+               path = filepath.Join(shmDir, a.shmPrefix+"-"+vppShmFile)
        }
 
-       // Watch folder if file does not exist yet
-       if !fileExists(path) {
-               watcher, err := fsnotify.NewWatcher()
-               if err != nil {
-                       return err
-               }
-               defer watcher.Close()
+       // check if file at the path exists
+       if _, err := os.Stat(path); err == nil {
+               // file exists, we are ready
+               return nil
+       } else if !os.IsNotExist(err) {
+               return err
+       }
 
-               if err := watcher.Add(watchedFolder); err != nil {
-                       return err
-               }
+       // file does not exist, start watching folder
+       watcher, err := fsnotify.NewWatcher()
+       if err != nil {
+               return err
+       }
+       defer watcher.Close()
+
+       if err := watcher.Add(shmDir); err != nil {
+               return err
+       }
 
-               for {
-                       ev := <-watcher.Events
-                       if ev.Name == path && (ev.Op&fsnotify.Create) == fsnotify.Create {
+       for {
+               ev := <-watcher.Events
+               if ev.Name == path {
+                       if (ev.Op & fsnotify.Create) == fsnotify.Create {
+                               // file was created, we are ready
                                break
                        }
                }
@@ -193,20 +213,11 @@ func (a *vppAPIClientAdapter) WaitReady() error {
        return nil
 }
 
-func fileExists(name string) bool {
-       if _, err := os.Stat(name); err != nil {
-               if os.IsNotExist(err) {
-                       return false
-               }
-       }
-       return true
-}
-
 //export go_msg_callback
 func go_msg_callback(msgID C.uint16_t, data unsafe.Pointer, size C.size_t) {
        // convert unsafe.Pointer to byte slice
-       slice := &reflect.SliceHeader{Data: uintptr(data), Len: int(size), Cap: int(size)}
-       byteArr := *(*[]byte)(unsafe.Pointer(slice))
+       sliceHeader := &reflect.SliceHeader{Data: uintptr(data), Len: int(size), Cap: int(size)}
+       byteSlice := *(*[]byte)(unsafe.Pointer(sliceHeader))
 
-       vppClient.callback(uint16(msgID), byteArr)
+       client.msgCallback(uint16(msgID), byteSlice)
 }