diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java
index e5bb3244f4c3098f3746147634d6c4c26528fea1..c90fbc6920c0ebec90256571eebbbc3aec183557 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java
@@ -29,10 +29,12 @@ import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceEnabled;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.EnumMap;
+import java.util.concurrent.locks.ReentrantLock;
 
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.InteropException;
 import com.oracle.truffle.api.interop.Message;
@@ -43,6 +45,7 @@ import com.oracle.truffle.r.ffi.impl.common.LibPaths;
 import com.oracle.truffle.r.ffi.impl.common.RFFIUtils;
 import com.oracle.truffle.r.ffi.impl.nfi.TruffleNFI_DLL.NFIHandle;
 import com.oracle.truffle.r.ffi.impl.upcalls.Callbacks;
+import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RContext.ContextKind;
@@ -77,6 +80,9 @@ class UnsafeAdapter {
 
 final class TruffleNFI_Context extends RFFIContext {
 
+    @CompilationFinal private static boolean hasAccessLock;
+    private static ReentrantLock accessLock;
+
     TruffleNFI_Context() {
         super(new TruffleNFI_C(), new TruffleNFI_Base(), new TruffleNFI_Call(), new TruffleNFI_DLL(), new TruffleNFI_UserRng(), new TruffleNFI_Zip(), new TruffleNFI_PCRE(), new TruffleNFI_Lapack(),
                         new TruffleNFI_Stats(), new TruffleNFI_Tools(), new TruffleNFI_REmbed(), new TruffleNFI_Misc());
@@ -231,6 +237,7 @@ final class TruffleNFI_Context extends RFFIContext {
     @Override
     public ContextState initialize(RContext context) {
         RFFIUtils.initializeTracing();
+        initializeLock();
         if (traceEnabled()) {
             traceDownCall("initialize");
         }
@@ -266,6 +273,13 @@ final class TruffleNFI_Context extends RFFIContext {
         }
     }
 
+    private static synchronized void initializeLock() {
+        hasAccessLock = FastROptions.SynchronizeNativeCode.getBooleanValue();
+        if (hasAccessLock && accessLock == null) {
+            accessLock = new ReentrantLock();
+        }
+    }
+
     @Override
     public void beforeDispose(RContext context) {
         switch (context.getKind()) {
@@ -302,4 +316,30 @@ final class TruffleNFI_Context extends RFFIContext {
     public DLLInfo getRLibDLLInfo() {
         return rlibDLLInfo;
     }
+
+    @Override
+    public void beforeUpcall(boolean canRunGc) {
+        super.beforeUpcall(canRunGc);
+        if (hasAccessLock) {
+            acquireLock();
+        }
+    }
+
+    @Override
+    public void afterUpcall(boolean canRunGc) {
+        super.afterUpcall(canRunGc);
+        if (hasAccessLock) {
+            releaseLock();
+        }
+    }
+
+    @TruffleBoundary
+    private void acquireLock() {
+        accessLock.lock();
+    }
+
+    @TruffleBoundary
+    private void releaseLock() {
+        accessLock.unlock();
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
index 982d68f8efb80f69c549df0d455d1b7be7985137..090cd5aa987d71ee1ac1da2c7155bc2e7643cdd2 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
@@ -61,6 +61,7 @@ public enum FastROptions {
     EmitTmpDir("The directory where to allocate temporary files with deparsed source code.", null, true),
     EmitTmpHashed("Use an SHA-256 hash as file name to reduce temporary file creation.", true),
     SpawnUsesPolyglot("use PolyglotEngine for .fastr.context.spwan", false),
+    SynchronizeNativeCode("allow only one thread to enter packages' native code", false),
 
     // Promises optimizations
     EagerEval("If enabled, overrides all other EagerEval switches (see EagerEvalHelper)", false),