concurrent.py 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. from threading import Lock
  2. class ResponsibleGenerator:
  3. """A generator that will help clean up when it is done being used."""
  4. __slots__ = ["cleanup", "gen"]
  5. def __init__(self, gen, cleanup):
  6. self.cleanup = cleanup
  7. self.gen = gen
  8. def __del__(self):
  9. self.cleanup()
  10. def __iter__(self):
  11. return self
  12. def __next__(self):
  13. return next(self.gen)
  14. class ConcurrentStore:
  15. """A store that allows concurrent reads and writes."""
  16. def __init__(self, store):
  17. self.store = store
  18. # number of calls to visit still in progress
  19. self.__visit_count = 0
  20. # lock for locking down the indices
  21. self.__lock = Lock()
  22. # lists for keeping track of added and removed triples while
  23. # we wait for the lock
  24. self.__pending_removes = []
  25. self.__pending_adds = []
  26. def add(self, triple):
  27. (s, p, o) = triple
  28. if self.__visit_count == 0:
  29. self.store.add((s, p, o))
  30. else:
  31. self.__pending_adds.append((s, p, o))
  32. def remove(self, triple):
  33. (s, p, o) = triple
  34. if self.__visit_count == 0:
  35. self.store.remove((s, p, o))
  36. else:
  37. self.__pending_removes.append((s, p, o))
  38. def triples(self, triple):
  39. (su, pr, ob) = triple
  40. g = self.store.triples((su, pr, ob))
  41. pending_removes = self.__pending_removes
  42. self.__begin_read()
  43. for s, p, o in ResponsibleGenerator(g, self.__end_read):
  44. if not (s, p, o) in pending_removes: # noqa: E713
  45. yield s, p, o
  46. for s, p, o in self.__pending_adds:
  47. if (
  48. (su is None or su == s)
  49. and (pr is None or pr == p)
  50. and (ob is None or ob == o)
  51. ):
  52. yield s, p, o
  53. def __len__(self):
  54. return self.store.__len__()
  55. def __begin_read(self):
  56. lock = self.__lock
  57. lock.acquire()
  58. self.__visit_count = self.__visit_count + 1
  59. lock.release()
  60. def __end_read(self):
  61. lock = self.__lock
  62. lock.acquire()
  63. self.__visit_count = self.__visit_count - 1
  64. if self.__visit_count == 0:
  65. pending_removes = self.__pending_removes
  66. while pending_removes:
  67. (s, p, o) = pending_removes.pop()
  68. try:
  69. self.store.remove((s, p, o))
  70. except: # noqa: E722
  71. # TODO: change to try finally?
  72. print(s, p, o, "Not in store to remove")
  73. pending_adds = self.__pending_adds
  74. while pending_adds:
  75. (s, p, o) = pending_adds.pop()
  76. self.store.add((s, p, o))
  77. lock.release()