personal_cloud
personal_cloud

Reputation: 4484

python: How to set F_NOTIFY from non-main thread?

I am trying to watch a directory using F_NOTIFY in Linux (Ubuntu 16, Python 3.5.2). However, it only works if I call fcntl from the main thread. Why are there no signals when I call fcntl from other threads? (I don't see any mention of threads in the python fcntl documentation. Also, if fcntl is failing, why doesn't it throw an exception?)

# Before running, create directory "a". To test, create a file in "a".

import fcntl, signal, os, threading

pipe_r, pipe_w = os.pipe()
def Unsafe_Handler(s,f):
    os.write(pipe_w, b' ')
signal.signal(signal.SIGIO, Unsafe_Handler)
signal.siginterrupt(signal.SIGIO, False)
def Handler():
    while True:
        os.read(pipe_r, 1)
        print("SIGNAL")
threading.Thread(target = Handler).start()

NOTIF_FLAGS = fcntl.DN_MODIFY | fcntl.DN_CREATE | fcntl.DN_MULTISHOT

def Watch_Dir(dn):
    fd = os.open(dn, os.O_RDONLY)
    fcntl.fcntl(fd, fcntl.F_SETSIG, 0)
    fcntl.fcntl(fd, fcntl.F_NOTIFY, NOTIF_FLAGS)
    print('Watching directory "%s", fd=%d' % (dn, fd))
    return fd

def Init():
    fd = Watch_Dir('a')


# this works
#Init()

# this doesn't work
t = threading.Thread(target = Init)
t.start()
t.join()


print("Awaiting signals...")
while True:
    signal.pause()

Why signal handling in Python requires threads

The Handler thread is required because of the following combination of facts:

  1. The handler attached with signal.signal can't safely do anything but write to a pipe.
  2. The main thread can't do real work either, for two reasons. First, because most blocking calls in the main thread will prevent the signal from being handled. Second, since the only allowed call is signal.pause(), there is no way to safely wake up the main thread without a race condition (signal arrives just before the signal.pause()) that could get the main thread stuck.

Why calling fcntl from the non-main thread is useful

This is simply because the main thread can't do real work, as already noted. (In my actual application, the handler needs to call fcntl. For this question I've created a simpler example though).

Upvotes: 0

Views: 39

Answers (0)

Related Questions