github.com/teepark/go-sysvipc@v0.0.0-20200817232735-d7ca6053ea29/sem.go (about)

     1  package sysvipc
     2  
     3  /*
     4  #include <sys/types.h>
     5  #include <sys/ipc.h>
     6  #include <sys/sem.h>
     7  int semget(key_t key, int nsems, int semflg);
     8  int semtimedop(int semid, struct sembuf *sops, size_t nsops, const struct timespec *timeout);
     9  
    10  union arg4 {
    11  	int             val;
    12  	struct semid_ds *buf;
    13  	unsigned short  *array;
    14  };
    15  int semctl_noarg(int semid, int semnum, int cmd) {
    16  	return semctl(semid, semnum, cmd);
    17  };
    18  int semctl_buf(int semid, int cmd, struct semid_ds *buf) {
    19  	union arg4 arg;
    20  	arg.buf = buf;
    21  	return semctl(semid, 0, cmd, arg);
    22  };
    23  int semctl_arr(int semid, int cmd, unsigned short *arr) {
    24  	union arg4 arg;
    25  	arg.array = arr;
    26  	return semctl(semid, 0, cmd, arg);
    27  };
    28  int semctl_val(int semid, int semnum, int cmd, int value) {
    29  	union arg4 arg;
    30  	arg.val = value;
    31  	return semctl(semid, semnum, cmd, arg);
    32  };
    33  */
    34  import "C"
    35  import (
    36  	"errors"
    37  	"time"
    38  )
    39  
    40  // SemaphoreSet is a kernel-maintained collection of semaphores.
    41  type SemaphoreSet struct {
    42  	id    int64
    43  	count uint
    44  }
    45  
    46  // GetSemSet creates or retrieves the semaphore set for a given IPC key.
    47  func GetSemSet(key, count int64, flags *SemSetFlags) (*SemaphoreSet, error) {
    48  	rc, err := C.semget(C.key_t(key), C.int(count), C.int(flags.flags()))
    49  	if rc == -1 {
    50  		return nil, err
    51  	}
    52  	return &SemaphoreSet{int64(rc), uint(count)}, nil
    53  }
    54  
    55  // Run applies a group of SemOps atomically.
    56  func (ss *SemaphoreSet) Run(ops *SemOps, timeout time.Duration) error {
    57  	var cto *C.struct_timespec
    58  	if timeout >= 0 {
    59  		cto = &C.struct_timespec{
    60  			tv_sec:  C.__time_t(timeout / time.Second),
    61  			tv_nsec: C.__syscall_slong_t(timeout % time.Second),
    62  		}
    63  	}
    64  
    65  	var opptr *C.struct_sembuf
    66  	if len(*ops) > 0 {
    67  		opptr = &(*ops)[0]
    68  	}
    69  
    70  	rc, err := C.semtimedop(C.int(ss.id), opptr, C.size_t(len(*ops)), cto)
    71  	if rc == -1 {
    72  		return err
    73  	}
    74  	return nil
    75  }
    76  
    77  // Getval retrieves the value of a single semaphore in the set
    78  func (ss *SemaphoreSet) Getval(num uint16) (int, error) {
    79  	val, err := C.semctl_noarg(C.int(ss.id), C.int(num), C.GETVAL)
    80  	if val == -1 {
    81  		return -1, err
    82  	}
    83  	return int(val), nil
    84  }
    85  
    86  // Setval sets the value of a single semaphore in the set
    87  func (ss *SemaphoreSet) Setval(num uint16, value int) error {
    88  	val, err := C.semctl_val(C.int(ss.id), C.int(num), C.SETVAL, C.int(value))
    89  	if val == -1 {
    90  		return err
    91  	}
    92  	return nil
    93  }
    94  
    95  // Getall retrieves the values of all the semaphores in the set
    96  func (ss *SemaphoreSet) Getall() ([]uint16, error) {
    97  	carr := make([]C.ushort, ss.count)
    98  
    99  	rc, err := C.semctl_arr(C.int(ss.id), C.GETALL, &carr[0])
   100  	if rc == -1 {
   101  		return nil, err
   102  	}
   103  
   104  	results := make([]uint16, ss.count)
   105  	for i, ci := range carr {
   106  		results[i] = uint16(ci)
   107  	}
   108  	return results, nil
   109  }
   110  
   111  // Setall sets the values of every semaphore in the set
   112  func (ss *SemaphoreSet) Setall(values []uint16) error {
   113  	if uint(len(values)) != ss.count {
   114  		return errors.New("sysvipc: wrong number of values for Setall")
   115  	}
   116  
   117  	carr := make([]C.ushort, ss.count)
   118  	for i, val := range values {
   119  		carr[i] = C.ushort(val)
   120  	}
   121  
   122  	rc, err := C.semctl_arr(C.int(ss.id), C.SETALL, &carr[0])
   123  	if rc == -1 {
   124  		return err
   125  	}
   126  	return nil
   127  }
   128  
   129  // Getpid returns the last process id to operate on the num-th semaphore
   130  func (ss *SemaphoreSet) Getpid(num uint16) (int, error) {
   131  	rc, err := C.semctl_noarg(C.int(ss.id), C.int(num), C.GETPID)
   132  	if rc == -1 {
   133  		return 0, err
   134  	}
   135  	return int(rc), nil
   136  }
   137  
   138  // GetNCnt returns the # of those blocked Decrementing the num-th semaphore
   139  func (ss *SemaphoreSet) GetNCnt(num uint16) (int, error) {
   140  	rc, err := C.semctl_noarg(C.int(ss.id), C.int(num), C.GETNCNT)
   141  	if rc == -1 {
   142  		return 0, err
   143  	}
   144  	return int(rc), nil
   145  }
   146  
   147  // GetZCnt returns the # of those blocked on WaitZero on the num-th semaphore
   148  func (ss *SemaphoreSet) GetZCnt(num uint16) (int, error) {
   149  	rc, err := C.semctl_noarg(C.int(ss.id), C.int(num), C.GETZCNT)
   150  	if rc == -1 {
   151  		return 0, err
   152  	}
   153  	return int(rc), nil
   154  }
   155  
   156  // Stat produces information about the semaphore set.
   157  func (ss *SemaphoreSet) Stat() (*SemSetInfo, error) {
   158  	sds := C.struct_semid_ds{}
   159  
   160  	rc, err := C.semctl_buf(C.int(ss.id), C.IPC_STAT, &sds)
   161  	if rc == -1 {
   162  		return nil, err
   163  	}
   164  
   165  	ssinf := SemSetInfo{
   166  		Perms: IpcPerms{
   167  			OwnerUID:   int(sds.sem_perm.uid),
   168  			OwnerGID:   int(sds.sem_perm.gid),
   169  			CreatorUID: int(sds.sem_perm.cuid),
   170  			CreatorGID: int(sds.sem_perm.cgid),
   171  			Mode:       uint16(sds.sem_perm.mode),
   172  		},
   173  		LastOp:     time.Unix(int64(sds.sem_otime), 0),
   174  		LastChange: time.Unix(int64(sds.sem_ctime), 0),
   175  		Count:      uint(sds.sem_nsems),
   176  	}
   177  	return &ssinf, nil
   178  }
   179  
   180  // Set updates parameters of the semaphore set.
   181  func (ss *SemaphoreSet) Set(ssi *SemSetInfo) error {
   182  	sds := &C.struct_semid_ds{
   183  		sem_perm: C.struct_ipc_perm{
   184  			uid:  C.__uid_t(ssi.Perms.OwnerUID),
   185  			gid:  C.__gid_t(ssi.Perms.OwnerGID),
   186  			mode: C.ushort(ssi.Perms.Mode & 0x1FF),
   187  		},
   188  	}
   189  
   190  	rc, err := C.semctl_buf(C.int(ss.id), C.IPC_SET, sds)
   191  	if rc == -1 {
   192  		return err
   193  	}
   194  	return nil
   195  }
   196  
   197  // Remove deletes the semaphore set.
   198  // This will also awake anyone blocked on the set with EIDRM.
   199  func (ss *SemaphoreSet) Remove() error {
   200  	rc, err := C.semctl_noarg(C.int(ss.id), 0, C.IPC_RMID)
   201  	if rc == -1 {
   202  		return err
   203  	}
   204  	return nil
   205  }
   206  
   207  // SemOps is a collection of operations submitted to SemaphoreSet.Run.
   208  type SemOps []C.struct_sembuf
   209  
   210  func NewSemOps() *SemOps {
   211  	sops := SemOps(make([]C.struct_sembuf, 0))
   212  	return &sops
   213  }
   214  
   215  // Increment adds an operation that will increase a semaphore's number.
   216  func (so *SemOps) Increment(num uint16, by int16, flags *SemOpFlags) error {
   217  	if by < 0 {
   218  		return errors.New("sysvipc: by must be >0. use Decrement")
   219  	} else if by == 0 {
   220  		return errors.New("sysvipc: by must be >0. use WaitZero")
   221  	}
   222  
   223  	*so = append(*so, C.struct_sembuf{
   224  		sem_num: C.ushort(num),
   225  		sem_op:  C.short(by),
   226  		sem_flg: C.short(flags.flags()),
   227  	})
   228  	return nil
   229  }
   230  
   231  // WaitZero adds and operation that will block until a semaphore's number is 0.
   232  func (so *SemOps) WaitZero(num uint16, flags *SemOpFlags) error {
   233  	*so = append(*so, C.struct_sembuf{
   234  		sem_num: C.ushort(num),
   235  		sem_op:  C.short(0),
   236  		sem_flg: C.short(flags.flags()),
   237  	})
   238  	return nil
   239  }
   240  
   241  // Decrement adds an operation that will decrease a semaphore's number.
   242  func (so *SemOps) Decrement(num uint16, by int16, flags *SemOpFlags) error {
   243  	if by <= 0 {
   244  		return errors.New("sysvipc: by must be >0. use WaitZero or Increment")
   245  	}
   246  
   247  	*so = append(*so, C.struct_sembuf{
   248  		sem_num: C.ushort(num),
   249  		sem_op:  C.short(-by),
   250  		sem_flg: C.short(flags.flags()),
   251  	})
   252  	return nil
   253  }
   254  
   255  // SemSetInfo holds meta information about a semaphore set.
   256  type SemSetInfo struct {
   257  	Perms      IpcPerms
   258  	LastOp     time.Time
   259  	LastChange time.Time
   260  	Count      uint
   261  }
   262  
   263  // SemSetFlags holds the options for a GetSemSet() call
   264  type SemSetFlags struct {
   265  	// Create controls whether to create the set if it doens't already exist.
   266  	Create bool
   267  
   268  	// Exclusive causes GetSemSet to fail if the semaphore set already exists
   269  	// (only useful with Create).
   270  	Exclusive bool
   271  
   272  	// Perms is the file-style (rwxrwxrwx) permissions with which to create the
   273  	// semaphore set (also only useful with Create).
   274  	Perms int
   275  }
   276  
   277  func (sf *SemSetFlags) flags() int64 {
   278  	if sf == nil {
   279  		return 0
   280  	}
   281  
   282  	var f int64 = int64(sf.Perms) & 0777
   283  	if sf.Create {
   284  		f |= int64(C.IPC_CREAT)
   285  	}
   286  	if sf.Exclusive {
   287  		f |= int64(C.IPC_EXCL)
   288  	}
   289  
   290  	return f
   291  }
   292  
   293  // SemOpFlags holds the options for SemOp methods
   294  type SemOpFlags struct {
   295  	// DontWait causes calls that would otherwise block
   296  	// to instead fail with syscall.EAGAIN
   297  	DontWait bool
   298  }
   299  
   300  func (so *SemOpFlags) flags() int64 {
   301  	if so == nil {
   302  		return 0
   303  	}
   304  
   305  	var f int64
   306  	if so.DontWait {
   307  		f |= int64(C.IPC_NOWAIT)
   308  	}
   309  
   310  	return f
   311  }