Skip to content

Commit 57a4e09

Browse files
committed
Channel: removed pseudoconstructor, which clearly improves the design and makes it easier to constomize
pool: in serial mode, created channels will be serial-only, which brings 15% of performance
1 parent 0974f87 commit 57a4e09

File tree

4 files changed

+56
-31
lines changed

4 files changed

+56
-31
lines changed

lib/git/async/channel.py

+23-13
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from util import (
88
AsyncQueue,
9+
SyncQueue,
910
ReadOnly
1011
)
1112

@@ -24,27 +25,19 @@ class Channel(object):
2425
2526
Create a new channel """
2627
__slots__ = tuple()
27-
28-
def __new__(cls, *args):
29-
if cls is Channel:
30-
if len(args) > 0:
31-
raise ValueError("Cannot take any arguments when creating a new channel")
32-
wc = WChannel()
33-
rc = RChannel(wc)
34-
return wc, rc
35-
# END constructor mode
36-
return object.__new__(cls)
3728

3829

3930
class WChannel(Channel):
40-
"""The write end of a channel"""
31+
"""The write end of a channel - it is thread-safe"""
4132
__slots__ = ('_queue')
4233

34+
# The queue to use to store the actual data
35+
QueueCls = AsyncQueue
36+
4337
def __init__(self):
4438
"""initialize this instance, able to hold max_items at once
4539
Write calls will block if the channel is full, until someone reads from it"""
46-
self._queue = AsyncQueue()
47-
40+
self._queue = self.QueueCls()
4841

4942
#{ Interface
5043
def write(self, item, block=True, timeout=None):
@@ -75,6 +68,12 @@ def closed(self):
7568
#} END interface
7669

7770

71+
class SerialWChannel(WChannel):
72+
"""A slightly faster version of a WChannel, which sacrificed thead-safety for
73+
performance"""
74+
QueueCls = SyncQueue
75+
76+
7877
class RChannel(Channel):
7978
"""The read-end of a corresponding write channel"""
8079
__slots__ = '_wc'
@@ -174,3 +173,14 @@ def read(self, count=0, block=True, timeout=None):
174173
#} END interface
175174

176175
#} END classes
176+
177+
#{ Constructors
178+
def mkchannel(wctype = WChannel, rctype = RChannel):
179+
"""Create a channel, which consists of one write end and one read end
180+
:return: tuple(write_channel, read_channel)
181+
:param wctype: The type of the write channel to instantiate
182+
:param rctype: The type of the read channel to instantiate"""
183+
wc = wctype()
184+
rc = rctype(wc)
185+
return wc, rc
186+
#} END constructors

lib/git/async/pool.py

+23-13
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from threading import Lock
77

88
from util import (
9-
SyncQueue,
109
AsyncQueue,
1110
DummyLock
1211
)
@@ -19,8 +18,9 @@
1918

2019
from graph import Graph
2120
from channel import (
22-
Channel,
21+
mkchannel,
2322
WChannel,
23+
SerialWChannel,
2424
RChannel
2525
)
2626

@@ -329,7 +329,8 @@ def _remove_task_if_orphaned(self, task):
329329

330330
#{ Interface
331331
def size(self):
332-
""":return: amount of workers in the pool"""
332+
""":return: amount of workers in the pool
333+
:note: method is not threadsafe !"""
333334
return self._num_workers
334335

335336
def set_size(self, size=0):
@@ -339,7 +340,9 @@ def set_size(self, size=0):
339340
340341
:return: self
341342
:param size: if 0, the pool will do all work itself in the calling thread,
342-
otherwise the work will be distributed among the given amount of threads
343+
otherwise the work will be distributed among the given amount of threads.
344+
If the size is 0, newly added tasks will use channels which are NOT
345+
threadsafe to optimize item throughput.
343346
344347
:note: currently NOT threadsafe !"""
345348
assert size > -1, "Size cannot be negative"
@@ -437,17 +440,29 @@ def add_task(self, task):
437440
the task will be considered orphaned and will be deleted on the next
438441
occasion."""
439442
# create a write channel for it
440-
wc, rc = Channel()
441-
rc = RPoolChannel(wc, task, self)
442-
task.set_wc(wc)
443+
wctype = WChannel
443444

444445
self._taskgraph_lock.acquire()
445446
try:
446447
self._taskorder_cache.clear()
447448
self._tasks.add_node(task)
449+
450+
# fix locks - in serial mode, the task does not need real locks
451+
# Additionally, use a non-threadsafe queue
452+
# This brings about 15% more performance, but sacrifices thread-safety
453+
# when reading from multiple threads.
454+
if self.size() == 0:
455+
task._slock = DummyLock()
456+
wctype = SerialWChannel
457+
# END improve locks
458+
459+
# setup the tasks channel
460+
wc = wctype()
461+
rc = RPoolChannel(wc, task, self)
462+
task.set_wc(wc)
448463
finally:
449464
self._taskgraph_lock.release()
450-
# END sync task addition
465+
# END sync task addition
451466

452467
# If the input channel is one of our read channels, we add the relation
453468
if isinstance(task, InputChannelTask):
@@ -462,11 +477,6 @@ def add_task(self, task):
462477
# END add task relation
463478
# END handle input channels for connections
464479

465-
# fix locks - in serial mode, the task does not need real locks
466-
if self.size() == 0:
467-
task._slock = DummyLock()
468-
# END improve locks
469-
470480
return rc
471481

472482
#} END interface

lib/git/async/util.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,14 @@ def get(self, block=True, timeout=None):
6666
def empty(self):
6767
return len(self) == 0
6868

69-
put = deque.append
69+
def set_writable(self, state):
70+
pass
71+
72+
def writable(self):
73+
return True
74+
75+
def put(self, item, block=True, timeout=None):
76+
self.append(item)
7077

7178

7279
class HSCondition(deque):

test/git/async/test_channel.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@ class TestChannels(TestBase):
88

99
def test_base(self):
1010
# creating channel yields a write and a read channal
11-
wc, rc = Channel()
12-
assert isinstance(wc, WChannel)
11+
wc, rc = mkchannel()
12+
assert isinstance(wc, WChannel) # default args
1313
assert isinstance(rc, RChannel)
1414

15-
# everything else fails
16-
self.failUnlessRaises(ValueError, Channel, 1, "too many args")
1715

1816
# TEST UNLIMITED SIZE CHANNEL - writing+reading is FIFO
1917
item = 1

0 commit comments

Comments
 (0)