Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

clocks.py 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. """
  2. kombu.clocks
  3. ============
  4. Logical Clocks and Synchronization.
  5. """
  6. from __future__ import absolute_import
  7. from threading import Lock
  8. from itertools import islice
  9. from operator import itemgetter
  10. from .five import zip
  11. __all__ = ['LamportClock', 'timetuple']
  12. R_CLOCK = '_lamport(clock={0}, timestamp={1}, id={2} {3!r})'
  13. class timetuple(tuple):
  14. """Tuple of event clock information.
  15. Can be used as part of a heap to keep events ordered.
  16. :param clock: Event clock value.
  17. :param timestamp: Event UNIX timestamp value.
  18. :param id: Event host id (e.g. ``hostname:pid``).
  19. :param obj: Optional obj to associate with this event.
  20. """
  21. __slots__ = ()
  22. def __new__(cls, clock, timestamp, id, obj=None):
  23. return tuple.__new__(cls, (clock, timestamp, id, obj))
  24. def __repr__(self):
  25. return R_CLOCK.format(*self)
  26. def __getnewargs__(self):
  27. return tuple(self)
  28. def __lt__(self, other):
  29. # 0: clock 1: timestamp 3: process id
  30. try:
  31. A, B = self[0], other[0]
  32. # uses logical clock value first
  33. if A and B: # use logical clock if available
  34. if A == B: # equal clocks use lower process id
  35. return self[2] < other[2]
  36. return A < B
  37. return self[1] < other[1] # ... or use timestamp
  38. except IndexError:
  39. return NotImplemented
  40. def __gt__(self, other):
  41. return other < self
  42. def __le__(self, other):
  43. return not other < self
  44. def __ge__(self, other):
  45. return not self < other
  46. clock = property(itemgetter(0))
  47. timestamp = property(itemgetter(1))
  48. id = property(itemgetter(2))
  49. obj = property(itemgetter(3))
  50. class LamportClock(object):
  51. """Lamport's logical clock.
  52. From Wikipedia:
  53. A Lamport logical clock is a monotonically incrementing software counter
  54. maintained in each process. It follows some simple rules:
  55. * A process increments its counter before each event in that process;
  56. * When a process sends a message, it includes its counter value with
  57. the message;
  58. * On receiving a message, the receiver process sets its counter to be
  59. greater than the maximum of its own value and the received value
  60. before it considers the message received.
  61. Conceptually, this logical clock can be thought of as a clock that only
  62. has meaning in relation to messages moving between processes. When a
  63. process receives a message, it resynchronizes its logical clock with
  64. the sender.
  65. .. seealso::
  66. * `Lamport timestamps`_
  67. * `Lamports distributed mutex`_
  68. .. _`Lamport Timestamps`: http://en.wikipedia.org/wiki/Lamport_timestamps
  69. .. _`Lamports distributed mutex`: http://bit.ly/p99ybE
  70. *Usage*
  71. When sending a message use :meth:`forward` to increment the clock,
  72. when receiving a message use :meth:`adjust` to sync with
  73. the time stamp of the incoming message.
  74. """
  75. #: The clocks current value.
  76. value = 0
  77. def __init__(self, initial_value=0, Lock=Lock):
  78. self.value = initial_value
  79. self.mutex = Lock()
  80. def adjust(self, other):
  81. with self.mutex:
  82. value = self.value = max(self.value, other) + 1
  83. return value
  84. def forward(self):
  85. with self.mutex:
  86. self.value += 1
  87. return self.value
  88. def sort_heap(self, h):
  89. """List of tuples containing at least two elements, representing
  90. an event, where the first element is the event's scalar clock value,
  91. and the second element is the id of the process (usually
  92. ``"hostname:pid"``): ``sh([(clock, processid, ...?), (...)])``
  93. The list must already be sorted, which is why we refer to it as a
  94. heap.
  95. The tuple will not be unpacked, so more than two elements can be
  96. present.
  97. Will return the latest event.
  98. """
  99. if h[0][0] == h[1][0]:
  100. same = []
  101. for PN in zip(h, islice(h, 1, None)):
  102. if PN[0][0] != PN[1][0]:
  103. break # Prev and Next's clocks differ
  104. same.append(PN[0])
  105. # return first item sorted by process id
  106. return sorted(same, key=lambda event: event[1])[0]
  107. # clock values unique, return first item
  108. return h[0]
  109. def __str__(self):
  110. return str(self.value)
  111. def __repr__(self):
  112. return '<LamportClock: {0.value}>'.format(self)