I am trying to post to a semaphore using JNA on a Linux machine. For some reason, I always receive a 22 error (invalid argument) even for this simple example. In my understanding, should the below code not open a POSIX semaphore, post to it and close it again?
public class Sample {
private static final int O_CREAT = 0x40;
public static void main(String[] args) throws Exception {
File notifier = new File("/tmp", "_test" + new Random().nextInt());
if (!notifier.isFile() && !notifier.createNewFile()) {
throw new IllegalStateException("Could not create notifier: " + notifier);
}
SempahoreLibrary library = Native.load("c", SempahoreLibrary.class);
Pointer semaphore = library.sem_open(notifier.getAbsolutePath(), O_CREAT, 666, 0);
try {
library.sem_post(semaphore);
} finally {
library.sem_close(semaphore);
}
}
interface SempahoreLibrary extends Library {
Pointer sem_open(String name, int flags, int mode, int value) throws LastErrorException;
int sem_post(Pointer pointer) throws LastErrorException;
int sem_close(Pointer pointer) throws LastErrorException;
}
}
I initially couldn't make it work with JNR either (strongly recommended over JNA), and got curious. Writing it in C helped.. :)
An strace on the C port made it clear you don't have to create a file upfront and then "map" the semaphore to it. Also using the full path is wrong, because semaphores are created in /dev/shm and the "/" in the path screws up everything:
futex(0x7f731b1190d0, FUTEX_WAKE_PRIVATE, 2147483647) = 0
openat(AT_FDCWD, "/dev/shm/sem.sema", O_RDWR|O_NOFOLLOW) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=32, ...}) = 0
So you should be able to remove the whole file/path creation and just use a regular non-path name for the semaphore in sem_open. Also the file mode should be octal, and you should make sure to also load the pthread library - it's required.
Here is a working example in C:
// clang -Wall sema.c -lpthread
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(int argc, char** argv)
{
sem_t* s = sem_open("notifier", O_CREAT, 0644, 0);
if (!s) {
perror("sem_open");
exit(errno);
}
printf("s: %p\n", s);
sem_post(s);
int value = -1;
sem_getvalue(s, &value);
printf("value: %d\n", value);
sem_wait(s);
sem_getvalue(s, &value);
printf("value: %d\n", value);
sem_close(s);
exit(EXIT_SUCCESS);
}
Here is a working Java version using JNR:
import jnr.ffi.LastError;
import jnr.ffi.LibraryLoader;
import jnr.ffi.Pointer;
import jnr.ffi.Runtime;
public class Semaphore
{
private static final int O_CREAT = 0x40;
public interface SempahoreLibrary
{
Pointer sem_open(String name, int flags, int mode, int value);
int sem_post(Pointer pointer);
int sem_close(Pointer pointer);
}
public static void main(String[] args) throws Exception
{
LibraryLoader<SempahoreLibrary> loader = LibraryLoader.create(SempahoreLibrary.class);
loader.library("c");
loader.library("pthread");
SempahoreLibrary library = loader.load();
jnr.ffi.Runtime runtime = Runtime.getRuntime(library);
Pointer semaphore = library.sem_open("notifier", O_CREAT, 0644, 0);
if (semaphore == null)
{
int errno = LastError.getLastError(runtime);
System.out.println("sem_open: " + errno);
System.exit(errno);
}
System.out.println("semaphore: " + Long.toHexString(semaphore.address()));
try
{
int error = library.sem_post(semaphore);
System.out.println("post: " + (error == 0 ? "OK" : LastError.getLastError(runtime)));
}
finally
{
int error = library.sem_close(semaphore);
System.out.println("close: " + (error == 0 ? "OK" : LastError.getLastError(runtime)));
}
}
}