DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations

Self-Pipe Trick

Ruslan Spivak user avatar by
Ruslan Spivak
·
Jun. 27, 12 · Interview
Like (0)
Save
Tweet
Share
6.12K Views

Join the DZone community and get the full member experience.

Join For Free

Imagine that your server process needs to simultaneously wait for I/O on multiple descriptors and also needs to wait for the delivery of a signal.
The problem is that you can’t safely mix select() with signals because there is a chance of race condition.

Consider the following code excerpt:

GOT_SIGNAL = False
 
def handler(signum, frame):
    global GOT_SIGNAL
    GOT_SIGNAL = True
 
...
 
signal.signal(signal.SIGUSR1, handler)
 
# What if the signal arrives at this point?
 
try:
    reads, writes, excs = select.select(rlist, wlist, elist)
except select.error as e:
    code, msg = e.args
    if code == errno.EINTR:
        if GOT_SIGNAL:
            print 'Got signal'
    else:
        raise

The problem here is that if the SIGUSR1 is delivered after setting the signal handler but before call to select then the select call will block and we won’t execute our application logic in response to the event thus effectively “missing” the signal (our application logic in this case is printing the message: Got signal).

That’s an example of possible nasty racing. Let’s simulate that with selsigrace.py

Start the program

$ python selsigrace.py
PID: 32324
Sleep for 10 secs

and send the USR1 signal to the PID(it’s different on every run) within the 10 second interval while the process is still sleeping:

$ kill -USR1 32324

 You should see the program produce additional line of output ‘Wake up and block in “select”‘ and block without exiting, no message “Got signal”:

$ python selsigrace.py
PID: 32324
Sleep for 10 secs
Wake up and block in "select"

 If you send yet another USR1 signal at this point then the select will be interrupted and the program will terminate with the message:

$ kill -USR1 32324
$ python selsigrace.py
PID: 32324
Sleep for 10 secs
Wake up and block in "select"
Got signal

Self-Pipe Trick is used to avoid race conditions when waiting for signals and calling select on a set of descriptors.

The following steps describe how to implement it:

  1. Create a pipe and change its read and write ends to be nonblocking
  2. Add the read end of the pipe to the read list of descriptors given to select
  3. Install a signal handler for the signal we’re concerned with. When the signal arrives the signal handler writes a byte of data to the pipe. Because the write end of the pipe is nonblocking we prevent the situation when signals flood the process, the pipe becomes full and the process blocks itself in the signal handler.
  4. When select successfully returns check if the read end of the pipe is in the readables list and if it is then our signal has arrived.
  5. When the signal arrives read all bytes that are in the pipe and execute any actions that have to be done in response to the signal delivery.

You can check out the implementation on GitHub and try it.

Start the selfpipe.py and send a USR1 signal to it. You should see it output a message Got signal and exit.

Signal

Published at DZone with permission of Ruslan Spivak, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Create Spider Chart With ReactJS
  • 5 Software Developer Competencies: How To Recognize a Good Programmer
  • Key Elements of Site Reliability Engineering (SRE)
  • Custom Validators in Quarkus

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: