Repository: numba/numba

Issue:
AWS Lambda OSError: [Errno 38] Function not implemented
## Reporting a bug

<!--

Before submitting a bug report please ensure that you can check off these boxes:

-->

- [ X] I am using the latest released version of Numba (most recent is visible in
 the change log (https://github.com/numba/numba/blob/master/CHANGE_LOG).



 AWS Lambda execution environment does not have /dev/shm (shared memory for processes) support. Please refer to https://aws.amazon.com/blogs/compute/parallel-processing-in-python-with-aws-lambda/

upgrade from 0.39.0 to 0.40.0 gave me the error below

```
File "/tmp/ca-parquet/numba/__init__.py", line 28, in 

from .npyufunc import vectorize, guvectorize, threading_layer

File "/tmp/ca-parquet/numba/npyufunc/__init__.py", line 4, in 

from .decorators import Vectorize, GUVectorize, vectorize, guvectorize

File "/tmp/ca-parquet/numba/npyufunc/decorators.py", line 6, in 

from .parallel import ParallelUFuncBuilder, ParallelGUFuncBuilder

File "/tmp/ca-parquet/numba/npyufunc/parallel.py", line 233, in 

_backend_init_process_lock = procRLock()

File "/var/lang/lib/python3.6/multiprocessing/context.py", line 72, in RLock

return RLock(ctx=self.get_context())

File "/var/lang/lib/python3.6/multiprocessing/synchronize.py", line 188, in __init__

SemLock.__init__(self, RECURSIVE_MUTEX, 1, 1, ctx=ctx)

File "/var/lang/lib/python3.6/multiprocessing/synchronize.py", line 60, in __init__

unlink_now)

OSError: [Errno 38] Function not implemented

```




Discussion:
Hmm...  I guess this was an accidental regression caused by reorganizing the thread backend.

@stuartarchibald: Can you see a way defer creating the multiprocessing locks?
hmmm, I've given this some thought, as with most things involving threads and forks it's challenging to reason about. Essentially, I think if the locks are deferred then it is likely ok, though as this is probably the 4th or 5th iteration of thinking about it... I may change my mind :)
* forking before the point of deferred locking/backend init would mean deferred locks would be in the children and a thread pool would init in each child. I *think* this is a behavioural change to the present state where the module level lock on import prevents this (assuming Numba is imported ahead of fork).
* forking after the point of deferred locking/backend init would be ok, the backend would have initialized on the parent and then it's just down to the backend fork handling behaviours as to whether this is supported and what happens in the child (child would see the parent deferred locks if the compiler pipeline is re-entered and e.g. `pthread_atfork()` handlers would make sure the backend is consistent).
* forking between the deferred lock and the backend launch is prevented by virtue of the compiler lock.

The current test suite covers the first two cases and may well hit the third, perhaps we try moving it to deferred and seeing if anything breaks? Failing this... we could just `try/except` on the lock init and then warn?


Resolving patch:
diff --git a/numba/npyufunc/parallel.py b/numba/npyufunc/parallel.py
--- a/numba/npyufunc/parallel.py
+++ b/numba/npyufunc/parallel.py
@@ -230,7 +230,23 @@ def build_gufunc_wrapper(py_func, cres, sin, sout, cache):
 
 
 _backend_init_thread_lock = threadRLock()
-_backend_init_process_lock = procRLock()
+try:
+    _backend_init_process_lock = procRLock()
+except OSError as e:
+    # probably lack of /dev/shm for semaphore writes, warn the user
+    msg =("Could not obtain multiprocessing lock due to OS level error: %s\n"
+          "A likely cause of this problem is '/dev/shm' is missing or"
+          "read-only such that necessary semaphores cannot be written.\n"
+          "*** The responsibility of ensuring multiprocessing safe access to "
+          "this initialization sequence/module import is deferred to the user! "
+          "***\n")
+    warnings.warn(msg % str(e))
+    from contextlib import contextmanager
+    @contextmanager
+    def nop():
+        yield
+    _backend_init_process_lock = nop()
+
 _is_initialized = False
 
 # this is set by _launch_threads


Test patch: