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 }