package com.oracle.truffle.llvm.runtime.pthread;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.llvm.runtime.LLVMLanguage;
import com.oracle.truffle.llvm.runtime.NodeFactory;
import com.oracle.truffle.llvm.runtime.datalayout.DataLayout;
import com.oracle.truffle.llvm.runtime.nodes.intrinsics.multithreading.LLVMPThreadStart;
import com.oracle.truffle.llvm.runtime.pointer.LLVMPointer;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public final class LLVMPThreadContext {
private final TruffleLanguage.Env env;
private final Object threadLock;
private final ConcurrentMap<Long, Object> threadReturnValueStorage;
private final ConcurrentMap<Long, Thread> threadStorage;
private volatile boolean isCreateThreadAllowed;
private int pThreadKey;
private final Object pThreadKeyLock;
private final ConcurrentMap<Integer, ConcurrentMap<Long, LLVMPointer>> pThreadKeyStorage;
private final ConcurrentMap<Integer, LLVMPointer> pThreadDestructorStorage;
private final CallTarget pthreadCallTarget;
public LLVMPThreadContext(TruffleLanguage.Env env, LLVMLanguage language, DataLayout dataLayout) {
this.env = env;
this.threadLock = new Object();
this.threadReturnValueStorage = new ConcurrentHashMap<>();
this.threadStorage = new ConcurrentHashMap<>();
this.pThreadKey = 0;
this.pThreadKeyLock = new Object();
this.pThreadKeyStorage = new ConcurrentHashMap<>();
this.pThreadDestructorStorage = new ConcurrentHashMap<>();
NodeFactory nodeFactory = language.getActiveConfiguration().createNodeFactory(language, dataLayout);
FrameDescriptor descriptor = LLVMPThreadStart.LLVMPThreadFunctionRootNode.createFrameDescriptor();
this.pthreadCallTarget = Truffle.getRuntime().createCallTarget(
new LLVMPThreadStart.LLVMPThreadFunctionRootNode(language, descriptor, nodeFactory));
this.isCreateThreadAllowed = true;
}
@TruffleBoundary
public void joinAllThreads() {
final Collection<Thread> threadsToJoin;
synchronized (threadLock) {
this.isCreateThreadAllowed = false;
threadsToJoin = threadStorage.values();
}
for (Thread createdThread : threadsToJoin) {
try {
createdThread.join();
} catch (InterruptedException e) {
}
}
}
public int createPThreadKey(LLVMPointer destructor) {
synchronized (pThreadKeyLock) {
pThreadKey++;
registerPThreadKey(pThreadKey, destructor);
return pThreadKey;
}
}
@TruffleBoundary
private void registerPThreadKey(int key, LLVMPointer destructor) {
pThreadDestructorStorage.put(key, destructor);
pThreadKeyStorage.put(key, new ConcurrentHashMap<>());
}
public int getNumberOfPthreadKeys() {
return pThreadKey;
}
@TruffleBoundary
public void deletePThreadKey(int keyId) {
synchronized (pThreadKeyLock) {
pThreadKeyStorage.remove(keyId);
pThreadDestructorStorage.remove(keyId);
}
}
@TruffleBoundary
public LLVMPointer getSpecific(int keyId) {
final ConcurrentMap<Long, LLVMPointer> value = pThreadKeyStorage.get(keyId);
if (value != null) {
final long threadId = Thread.currentThread().getId();
return value.get(threadId);
}
return null;
}
@TruffleBoundary
public boolean setSpecific(int keyId, LLVMPointer value) {
final ConcurrentMap<Long, LLVMPointer> specificStore = pThreadKeyStorage.get(keyId);
if (specificStore != null) {
specificStore.put(Thread.currentThread().getId(), value);
return true;
}
return false;
}
@TruffleBoundary
public LLVMPointer getAndRemoveSpecificUnlessNull(int keyId) {
final ConcurrentMap<Long, LLVMPointer> value = pThreadKeyStorage.get(keyId);
if (value != null) {
final long threadId = Thread.currentThread().getId();
final LLVMPointer keyMapping = value.get(threadId);
if (keyMapping != null && !keyMapping.isNull()) {
value.remove(threadId);
return keyMapping;
}
}
return null;
}
@TruffleBoundary
public LLVMPointer getDestructor(int keyId) {
return pThreadDestructorStorage.get(keyId);
}
@TruffleBoundary
public Thread createThread(Runnable runnable) {
synchronized (threadLock) {
if (isCreateThreadAllowed) {
final Thread thread = env.createThread(runnable);
threadStorage.put(thread.getId(), thread);
return thread;
} else {
return null;
}
}
}
@TruffleBoundary
public Thread getThread(long threadID) {
return threadStorage.get(threadID);
}
@TruffleBoundary
public void clearThreadId() {
threadStorage.remove(Thread.currentThread().getId());
}
@TruffleBoundary
public void setThreadReturnValue(long threadId, Object value) {
threadReturnValueStorage.put(threadId, value);
}
@TruffleBoundary
public Object getThreadReturnValue(long threadId) {
return threadReturnValueStorage.get(threadId);
}
public CallTarget getPthreadCallTarget() {
return pthreadCallTarget;
}
}