Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Add initial documentation on SELF-PIPE Trick

  • Loading branch information...
commit aa5fa6a28c24404e6b9f1898fd882ea690eea152 1 parent e44eea4
authored

Showing 2 changed files with 168 additions and 2 deletions. Show diff stats Hide diff stats

  1. 91  README.rst
  2. 79  selsigrace.py
91  README.rst
Source Rendered
@@ -117,6 +117,95 @@ an RST as a response, ignores it and tries to write to the socket:
117 117
         s.send('hello')
118 118
     socket.error: [Errno 32] Broken pipe
119 119
 
  120
+
  121
+SELF-PIPE Trick
  122
+~~~~~~~~~~~~~~~
  123
+
  124
+If a process needs to monitor several descriptors for I/O and wait
  125
+for the delivery of a signal then a race condition can happen.
  126
+
  127
+Consider the following code excerpt:
  128
+
  129
+::
  130
+
  131
+    GOT_SIGNAL = False
  132
+
  133
+    def handler(signum, frame):
  134
+        global GOT_SIGNAL
  135
+        GOT_SIGNAL = True
  136
+
  137
+    ...
  138
+
  139
+    signal.signal(signal.SIGUSR1, handler)
  140
+
  141
+    # What if the signal arrives at this point?
  142
+
  143
+    try:
  144
+        readables, writables, exceptions = select.select(rlist, wlist, elist)
  145
+    except select.error as e:
  146
+        code, msg = e.args
  147
+        if code == errno.EINTR:
  148
+            if GOT_SIGNAL:
  149
+                print 'Got signal'
  150
+        else:
  151
+            raise
  152
+
  153
+The problem here is that if the *SIGUSR1* is delivered after setting
  154
+the signal handler but before call to *select* then the *select*
  155
+call **will block** and we won't execute our application logic in
  156
+response to the event thus effectively *"missing"* the signal (our
  157
+application logic in this case is printing the message: *Got signal*).
  158
+
  159
+That's an example of possible nasty racing. Let's simulate that with `selsigrace.py <https://github.com/rspivak/csdesign/blob/master/selsigrace.py>`_
  160
+
  161
+Start the program
  162
+
  163
+::
  164
+
  165
+    $ python selsigrace.py
  166
+    PID: 32324
  167
+    Sleep for 10 secs
  168
+
  169
+and send the *USR1* signal to the PID(it's different on every run)
  170
+within the 10 second interval while the process is still sleeping:
  171
+
  172
+::
  173
+
  174
+    $ kill -USR1 32324
  175
+
  176
+
  177
+You should see the program produce additional line of output
  178
+'Wake up and block in "select"' and **block without exiting**, no
  179
+message "Got signal":
  180
+
  181
+::
  182
+
  183
+    $ python selsigrace.py
  184
+    PID: 32324
  185
+    Sleep for 10 secs
  186
+    Wake up and block in "select"
  187
+
  188
+If you send yet another *USR1* signal at this point then the *select*
  189
+will be interrupted and the program will terminate with a message:
  190
+
  191
+::
  192
+
  193
+    $ kill -USR1 32324
  194
+
  195
+::
  196
+
  197
+    $ python selsigrace.py
  198
+    PID: 32324
  199
+    Sleep for 10 secs
  200
+    Wake up and block in "select"
  201
+    Got signal
  202
+
  203
+*Self-Pipe Trick* is used to avoid race conditions when waiting for
  204
+signals and calling *select* on a set of descriptors.
  205
+
  206
+XXX: Code
  207
+
  208
+
120 209
 Roadmap
121 210
 -------
122 211
 
@@ -128,8 +217,6 @@ Roadmap
128 217
 
129 218
 - TCP Prethreaded Server
130 219
 
131  
-- Miscellanea, SELF-PIPE Trick
132  
-
133 220
 - Miscellanea, **sendfile** system call
134 221
 
135 222
 - Miscellanea, TCP_CORK socket option
79  selsigrace.py
... ...
@@ -0,0 +1,79 @@
  1
+###############################################################################
  2
+#
  3
+# Copyright (c) 2012 Ruslan Spivak
  4
+#
  5
+# Permission is hereby granted, free of charge, to any person obtaining a copy
  6
+# of this software and associated documentation files (the "Software"), to deal
  7
+# in the Software without restriction, including without limitation the rights
  8
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9
+# copies of the Software, and to permit persons to whom the Software is
  10
+# furnished to do so, subject to the following conditions:
  11
+#
  12
+# The above copyright notice and this permission notice shall be included in
  13
+# all copies or substantial portions of the Software.
  14
+#
  15
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21
+# THE SOFTWARE.
  22
+#
  23
+###############################################################################
  24
+
  25
+"""
  26
+Illustrates a problem with a race condition that might happen if a process
  27
+needs to monitor several descriptors for I/O and wait for the delivery of
  28
+a signal.
  29
+"""
  30
+
  31
+__author__ = 'Ruslan Spivak <ruslan.spivak@gmail.com>'
  32
+
  33
+import os
  34
+import sys
  35
+import time
  36
+import errno
  37
+import select
  38
+import signal
  39
+
  40
+GOT_SIGNAL = False
  41
+
  42
+
  43
+def handler(signum, frame):
  44
+    global GOT_SIGNAL
  45
+    GOT_SIGNAL = True
  46
+
  47
+
  48
+def main():
  49
+    print 'PID: %s' % os.getpid()
  50
+
  51
+    signal.signal(signal.SIGUSR1, handler)
  52
+
  53
+    # read, write, exception lists with descriptors to poll
  54
+    rlist, wlist, elist = [sys.stdin.fileno()], [], []
  55
+
  56
+    print 'Sleep for 10 secs'
  57
+    time.sleep(10)
  58
+    print 'Wake up and block in "select"'
  59
+
  60
+    #
  61
+    # Nasty racing can happen at this point if the signal arrives before
  62
+    # the call to 'select' - the call won't be interrupted and 'select'
  63
+    # will block
  64
+    #
  65
+
  66
+    # block in select
  67
+    try:
  68
+        readables, writables, exceptions = select.select(rlist, wlist, elist)
  69
+    except select.error as e:
  70
+        code, msg = e.args
  71
+        if code == errno.EINTR:
  72
+            if GOT_SIGNAL:
  73
+                print 'Got signal'
  74
+        else:
  75
+            raise
  76
+
  77
+
  78
+if __name__ == '__main__':
  79
+    main()

0 notes on commit aa5fa6a

Please sign in to comment.
Something went wrong with that request. Please try again.