Posted to tcl by JMN at Thu Jul 31 03:29:23 GMT 2025view raw

  1. import sys
  2. import time
  3. import asyncio
  4. from subprocess import Popen, PIPE
  5.  
  6. RESET = "\033[0m"
  7. C = "\033[32m" # Child color green
  8. P = "\033[33m" # Parent color yellow
  9.  
  10.  
  11. def usage():
  12. print("Usage:")
  13. print(" stdin_race.py pump <persecond> <maxcount>")
  14. print(" stdin_race.py parent")
  15. print(" stdin_race.py child <delay_ms>")
  16. print("\nExample:")
  17. print(" python stdin_race.py pump 35 50 | python stdin_race.py parent")
  18. sys.exit(1)
  19.  
  20.  
  21. async def pump(persecond, maxcount):
  22. if persecond > 1000:
  23. print("WARNING: (>1000) sub-millisecond scheduling not available - will go full speed", file=sys.stderr)
  24. await asyncio.sleep(0.5)
  25.  
  26. counter = -1
  27. ms = 1000 / persecond
  28.  
  29. async def pump_emit():
  30. nonlocal counter
  31. try:
  32. counter += 1
  33. print(f".{counter}", end="", flush=True)
  34. except BrokenPipeError:
  35. return False
  36. return True
  37.  
  38. async def pump_schedule():
  39. nonlocal counter
  40. while maxcount <= 0 or counter < maxcount - 1:
  41. if not await pump_emit():
  42. break
  43. await asyncio.sleep(ms / 1000)
  44. print("pump-done", file=sys.stderr, flush=True)
  45.  
  46. await pump_schedule()
  47.  
  48.  
  49. async def parent():
  50. print(f"{P}parent{RESET}", file=sys.stderr, flush=True)
  51. await asyncio.sleep(0.25)
  52.  
  53. # Read the first chunk from stdin
  54. parent_chunk1 = sys.stdin.read(8)
  55. print(f"{P}{parent_chunk1}{RESET}", file=sys.stderr, flush=True)
  56.  
  57. # Launch the child process
  58. child_proc = Popen(
  59. [sys.executable, __file__, "child", "150"],
  60. stdin=PIPE, stdout=PIPE, stderr=sys.stderr, text=True
  61. )
  62.  
  63. # Forward stdin to the child process
  64. while True:
  65. chunk = sys.stdin.read(1)
  66. if not chunk:
  67. break
  68. print(f"{chunk}", file=sys.stdout, end='', flush=True)
  69.  
  70. # Wait for the child process to finish
  71. child_proc.stdin.close()
  72. child_proc.wait()
  73.  
  74. print("parent-tail-read", file=sys.stderr, flush=True)
  75. while True:
  76. chunk = sys.stdin.read(1)
  77. if not chunk:
  78. break
  79. print(chunk, end="", file=sys.stderr, flush=True)
  80.  
  81. print(f"\n{P}parent-done{RESET}", flush=True)
  82.  
  83.  
  84. async def child(delay_ms):
  85. print(f"\n{C}child{RESET}", file=sys.stderr, flush=True)
  86. await asyncio.sleep(delay_ms / 1000)
  87.  
  88. # Read from stdin
  89. chunk = sys.stdin.read(16)
  90. print(f"{C}{chunk}{RESET}", file=sys.stderr, flush=True)
  91.  
  92. print("child-done", file=sys.stderr, flush=True)
  93. sys.exit(0)
  94.  
  95.  
  96. def main():
  97. if len(sys.argv) < 2:
  98. usage()
  99.  
  100. role = sys.argv[1]
  101. if role == "pump":
  102. if len(sys.argv) != 4:
  103. usage()
  104. persecond = int(sys.argv[2])
  105. maxcount = int(sys.argv[3])
  106. asyncio.run(pump(persecond, maxcount))
  107. elif role == "parent":
  108. if len(sys.argv) != 2:
  109. usage()
  110. asyncio.run(parent())
  111. elif role == "child":
  112. if len(sys.argv) != 3:
  113. usage()
  114. delay_ms = int(sys.argv[2])
  115. asyncio.run(child(delay_ms))
  116. else:
  117. usage()
  118.  
  119.  
  120. if __name__ == "__main__":
  121. main()

Add a comment

Please note that this site uses the meta tags nofollow,noindex for all pages that contain comments.
Items are closed for new comments after 1 week