Skip to content

Commit 5c024b7

Browse files
committed
Now the code seems to be really threadsafe. Also added OS-specific code, and fixed miscellaneous bugs.
1 parent 02913c3 commit 5c024b7

10 files changed

Lines changed: 406 additions & 131 deletions

File tree

‎Xlib/display.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: display.py,v 1.7 2000-08-22 14:06:22 petli Exp $
1+
# $Id: display.py,v 1.8 2000-09-06 01:51:34 petli Exp $
22
#
33
# Xlib.display -- high level display object
44
#
@@ -314,7 +314,7 @@ def open_font(self, name):
314314
self.display.free_resource_id(fid)
315315
return None
316316
else:
317-
return fontable.Font(self.display, fid, owner = 1)
317+
return xobject.fontable.Font(self.display, fid, owner = 1)
318318

319319
def list_fonts(self, pattern, max_names):
320320
r = request.ListFonts(display = self.display,

‎Xlib/protocol/display.py‎

Lines changed: 112 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: display.py,v 1.5 2000-08-21 10:03:45 petli Exp $
1+
# $Id: display.py,v 1.6 2000-09-06 01:51:34 petli Exp $
22
#
33
# Xlib.protocol.display -- core display communication
44
#
@@ -20,94 +20,33 @@
2020

2121
# Standard modules
2222
import sys
23-
import os
2423
import select
25-
import string
2624
import struct
27-
import socket
28-
import re
29-
import fcntl
30-
import FCNTL
3125

3226
# Xlib modules
3327
from Xlib import error
3428

35-
from Xlib.support import lock
29+
from Xlib.support import lock, connect
3630

3731
# Xlib.protocol modules
3832
import rq
3933
import event
4034

41-
display_re = re.compile(r'^([-a-zA-Z0-9._]*):([0-9]+)(\.([0-9]+))?$')
42-
4335
class Display:
4436
resource_classes = {}
4537
extension_major_opcodes = {}
4638
error_classes = error.xerror_class.copy()
4739
event_classes = event.event_class.copy()
4840

4941
def __init__(self, display = None):
50-
# Use $DISPLAY if display isn't provided
51-
if display is None:
52-
display = os.environ.get('DISPLAY', '')
53-
54-
m = display_re.match(display)
55-
if not m:
56-
raise error.DisplayNameError(display)
57-
58-
self.display_name = display
59-
host = m.group(1)
60-
dno = int(m.group(2))
61-
screen = m.group(4)
62-
if screen:
63-
self.default_screen = int(screen)
64-
else:
65-
self.default_screen = 0
42+
name, host, displayno, screenno = connect.get_display(display)
6643

67-
try:
68-
# If hostname (or IP) is provided, use TCP socket
69-
if host:
70-
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
71-
self.socket.connect((host, 6000 + dno))
44+
self.display_name = name
45+
self.default_screen = screenno
7246

73-
# Else use Unix socket
74-
else:
75-
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
76-
self.socket.connect('/tmp/.X11-unix/X%d' % dno)
77-
except socket.error, val:
78-
raise error.DisplayConnectionError(display, str(val))
47+
self.socket = connect.get_socket(name, host, displayno)
7948

80-
# Make sure that the connection isn't inherited in child processes
81-
fcntl.fcntl(self.socket.fileno(), FCNTL.F_SETFD, FCNTL.FD_CLOEXEC)
82-
83-
# Find authorization cookie
84-
authorization = ('', '')
85-
try:
86-
# We could parse .Xauthority, but xauth is simpler
87-
# although more inefficient
88-
data = os.popen('xauth list %s 2>/dev/null' % self.display_name).read()
89-
90-
# If there's a cookie, it is of the format
91-
# DISPLAY SCHEME COOKIE
92-
# We're interested in the two last parts for the
93-
# connection establishment
94-
lines = string.split(data, '\n')
95-
if len(lines) >= 1:
96-
parts = string.split(lines[0], None, 2)
97-
if len(parts) == 3:
98-
scheme = parts[1]
99-
hexauth = parts[2]
100-
auth = ''
101-
102-
# Translate hexcode into binary
103-
for i in range(0, len(hexauth), 2):
104-
auth = auth + chr(string.atoi(hexauth[i:i+2], 16))
105-
106-
authorization = (scheme, auth)
107-
108-
except os.error:
109-
pass
110-
49+
auth_name, auth_data = connect.get_auth(name, host, displayno)
11150

11251
# Internal structures for communication, grouped
11352
# by their function and locks
@@ -130,7 +69,9 @@ def __init__(self, display = None):
13069
# Send-and-recieve loop, see function send_and_recive
13170
# for a detailed explanation
13271
self.send_recv_lock = lock.allocate_lock()
133-
self.send_recv_active = 0
72+
self.send_active = 0
73+
self.recv_active = 0
74+
13475
self.event_waiting = 0
13576
self.event_wait_lock = lock.allocate_lock()
13677
self.request_waiting = 0
@@ -168,8 +109,8 @@ def __init__(self, display = None):
168109
byte_order = order,
169110
protocol_major = 11,
170111
protocol_minor = 0,
171-
auth_prot_name = authorization[0],
172-
auth_prot_data = authorization[1])
112+
auth_prot_name = auth_name,
113+
auth_prot_data = auth_data)
173114

174115
# Did connection fail?
175116
if r.status != 1:
@@ -403,9 +344,15 @@ def send_and_recv(self, flush = None, event = None, request = None):
403344
fulfilled when the function returns, so the caller has to loop
404345
until it is finished.
405346
"""
347+
348+
# We go to sleep if there is already a thread doing what we
349+
# want to do:
350+
351+
# If flushing or waiting for a request we want to send
352+
# If waiting for an event we want to recv
406353

407-
# There is an active send_and_recieve, go to sleep
408-
if self.send_recv_active:
354+
if (((flush or request is not None) and self.send_active)
355+
or (event and self.recv_active)):
409356

410357
# Signal that we are waiting for something. These locks
411358
# together with the *_waiting variables are used as
@@ -460,60 +407,86 @@ def send_and_recv(self, flush = None, event = None, request = None):
460407
return
461408

462409

463-
# We're the only active send_and_recv, so perform
464-
# all our needed network stuff.
465-
466-
# Tell other threads that we are active, and lock
467-
# all wait locks to give later threads something to
468-
# hand on.
469-
470-
self.send_recv_active = 1
410+
# There's no thread doing what we need to do. Find out exactly
411+
# what to do
471412

472-
# Finally release send_recv and start doing some useful work
473-
self.send_recv_lock.release()
413+
# There must always be some thread recieving data, but it must not
414+
# necessarily be us
415+
416+
if not self.recv_active:
417+
recieving = 1
418+
self.recv_active = 1
419+
else:
420+
recieving = 0
474421

475422
flush_bytes = None
423+
sending = 0
476424

477425
# Loop, recieving and sending data.
478426
while 1:
479427

480-
# Turn all requests on request queue into binary form
481-
# and append them to self.data_send
482-
483-
self.request_queue_lock.acquire()
484-
485-
for req, wait in self.request_queue:
486-
self.data_send = self.data_send + req._binary
487-
if wait:
488-
self.sent_requests.append(req)
428+
# We might want to start sending data
429+
if sending or not self.send_active:
489430

490-
del self.request_queue[:]
491-
492-
self.request_queue_lock.release()
431+
# Turn all requests on request queue into binary form
432+
# and append them to self.data_send
433+
434+
self.request_queue_lock.acquire()
435+
for req, wait in self.request_queue:
436+
self.data_send = self.data_send + req._binary
437+
if wait:
438+
self.sent_requests.append(req)
439+
440+
del self.request_queue[:]
441+
self.request_queue_lock.release()
442+
443+
# If there now is data to send, mark us as senders
444+
445+
if self.data_send:
446+
self.send_active = 1
447+
sending = 1
448+
else:
449+
self.send_active = 0
450+
sending = 0
451+
452+
# We've done all setup, so release the lock and start waiting
453+
# for the network to fire up
454+
self.send_recv_lock.release()
493455

494456
# If we're flushing, figure out how many bytes we
495457
# have to send so that we're not caught in an interminable
496458
# loop if other threads continuously append requests.
497459
if flush and flush_bytes is None:
498460
flush_bytes = self.data_sent_bytes + len(self.data_send)
499461

500-
# Wait for socket to be ready for sending data (if any)
501-
# and recieving data (always).
502-
if self.data_send:
503-
writeset = [self.socket]
504-
else:
505-
writeset = []
506462

507463
try:
464+
# We're only checking for the socket to be writable
465+
# if we're the sending thread. We always check for it
466+
# to become readable: either we are the recieving thread
467+
# and should take care of the data, or the recieving thread
468+
# might finish recieving after having read the data
469+
470+
if sending:
471+
writeset = [self.socket]
472+
else:
473+
writeset = []
474+
508475
rs, ws, es = select.select([self.socket], writeset, [])
509476

510477
# Ignore errors caused by a signal recieved while blocking.
511478
# All other errors are re-raised.
512479
except select.error, err:
513480
if err[0] != errno.EINTR:
514481
raise err
482+
483+
# We must lock send_and_recv before we can loop to
484+
# the start of the loop
485+
486+
self.send_recv_lock.acquire()
515487
continue
516488

489+
517490
# Socket is ready for sending data, send as much as possible.
518491
if ws:
519492
try:
@@ -526,23 +499,39 @@ def send_and_recv(self, flush = None, event = None, request = None):
526499
self.data_sent_bytes = self.data_sent_bytes + i
527500

528501

529-
# There is data to read. Do so and parse recieved data.
502+
# There is data to read
530503
gotreq = 0
531504
if rs:
532-
try:
533-
recv = self.socket.recv(2048)
534-
except socket.error, err:
535-
self.close_internal('server: %s' % err[1])
536-
raise self.socket_error
537505

538-
if not recv:
539-
# Clear up, set a connection closed indicator and raise it
540-
self.close_internal('server')
541-
raise self.socket_error
542-
543-
self.data_recv = self.data_recv + recv
544-
gotreq = self.parse_response(request)
506+
# We're the recieving thread, parse the data
507+
if recieving:
508+
try:
509+
recv = self.socket.recv(2048)
510+
except socket.error, err:
511+
self.close_internal('server: %s' % err[1])
512+
raise self.socket_error
513+
514+
if not recv:
515+
# Clear up, set a connection closed indicator and raise it
516+
self.close_internal('server')
517+
raise self.socket_error
518+
519+
self.data_recv = self.data_recv + recv
520+
gotreq = self.parse_response(request)
521+
522+
# Otherwise return, allowing the calling thread to figure
523+
# out if it has got the data it needs
524+
else:
525+
# We must be a sending thread if we're here, so reset
526+
# that indicator.
527+
self.send_recv_lock.acquire()
528+
self.send_active = 0
529+
self.send_recv_lock.release()
530+
531+
# And return to the caller
532+
return
545533

534+
546535
# There are three different end of send-recv-loop conditions.
547536
# However, we don't leave the loop immediately, instead we
548537
# try to send and recieve any data that might be left. We
@@ -561,16 +550,22 @@ def send_and_recv(self, flush = None, event = None, request = None):
561550
if request is not None and gotreq:
562551
break
563552

564-
# Else there's still data which must be sent,
565-
# so loop around to another blocking select
553+
# Else there's may still data which must be sent, or
554+
# we haven't got the data we waited for. Lock and loop
555+
556+
self.send_recv_lock.acquire()
566557

567558

568559
# We have accomplished the callers request.
569560
# Record that there are now no active send_and_recv,
570561
# and wake up all waiting thread
571562

572563
self.send_recv_lock.acquire()
573-
self.send_recv_active = 0
564+
565+
if sending:
566+
self.send_active = 0
567+
if recieving:
568+
self.recv_active = 0
574569

575570
if self.event_waiting:
576571
self.event_waiting = 0

‎Xlib/protocol/event.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: event.py,v 1.2 2000-08-07 10:30:19 petli Exp $
1+
# $Id: event.py,v 1.3 2000-09-06 01:51:34 petli Exp $
22
#
33
# Xlib.protocol.event -- definitions of core events
44
#
@@ -41,7 +41,7 @@ class KeyButtonPointer(rq.Event):
4141
rq.Card16('sequence_number'),
4242
rq.Card32('time'),
4343
rq.Window('root'),
44-
rq.Window('event'),
44+
rq.Window('window'),
4545
rq.Window('child', (X.NONE, )),
4646
rq.Int16('root_x'),
4747
rq.Int16('root_y'),
@@ -74,7 +74,7 @@ class EnterLeave(rq.Event):
7474
rq.Card16('sequence_number'),
7575
rq.Card32('time'),
7676
rq.Window('root'),
77-
rq.Window('event'),
77+
rq.Window('window'),
7878
rq.Window('child', (X.NONE, )),
7979
rq.Int16('root_x'),
8080
rq.Int16('root_y'),
@@ -143,7 +143,7 @@ class NoExposure(rq.Event):
143143
_fields = rq.Struct( rq.Card8('type'),
144144
rq.Pad(1),
145145
rq.Card16('sequence_number'),
146-
rq.Drawable('drawable'),
146+
rq.Drawable('window'),
147147
rq.Card16('minor_event'),
148148
rq.Card8('major_event'),
149149
rq.Pad(21),

0 commit comments

Comments
 (0)