/rpython/rlib/rtime.py

https://bitbucket.org/pypy/pypy/ · Python · 236 lines · 194 code · 30 blank · 12 comment · 42 complexity · c994ccf63954d851f3558771c25a0c70 MD5 · raw file

  1. """
  2. RPython implementations of time.time(), time.clock(), time.select().
  3. """
  4. import sys
  5. import math
  6. import time as pytime
  7. from rpython.translator.tool.cbuild import ExternalCompilationInfo
  8. from rpython.rtyper.tool import rffi_platform
  9. from rpython.rtyper.lltypesystem import rffi, lltype
  10. from rpython.rlib.objectmodel import register_replacement_for
  11. from rpython.rlib.rarithmetic import intmask, UINT_MAX
  12. from rpython.rlib import rposix
  13. _WIN32 = sys.platform.startswith('win')
  14. if _WIN32:
  15. TIME_H = 'time.h'
  16. FTIME = '_ftime64'
  17. STRUCT_TIMEB = 'struct __timeb64'
  18. includes = ['winsock2.h', 'windows.h',
  19. TIME_H, 'sys/types.h', 'sys/timeb.h']
  20. need_rusage = False
  21. else:
  22. TIME_H = 'sys/time.h'
  23. FTIME = 'ftime'
  24. STRUCT_TIMEB = 'struct timeb'
  25. includes = [TIME_H, 'time.h', 'errno.h', 'sys/select.h',
  26. 'sys/types.h', 'unistd.h',
  27. 'sys/time.h', 'sys/resource.h']
  28. if not sys.platform.startswith("openbsd"):
  29. includes.append('sys/timeb.h')
  30. need_rusage = True
  31. eci = ExternalCompilationInfo(includes=includes)
  32. class CConfig:
  33. _compilation_info_ = eci
  34. TIMEVAL = rffi_platform.Struct('struct timeval', [('tv_sec', rffi.INT),
  35. ('tv_usec', rffi.INT)])
  36. HAVE_GETTIMEOFDAY = rffi_platform.Has('gettimeofday')
  37. HAVE_FTIME = rffi_platform.Has(FTIME)
  38. if need_rusage:
  39. RUSAGE = rffi_platform.Struct('struct rusage', [('ru_utime', TIMEVAL),
  40. ('ru_stime', TIMEVAL)])
  41. if sys.platform.startswith('freebsd') or sys.platform.startswith('netbsd'):
  42. libraries = ['compat']
  43. elif sys.platform == 'linux2':
  44. libraries = ['rt']
  45. else:
  46. libraries = []
  47. class CConfigForFTime:
  48. _compilation_info_ = ExternalCompilationInfo(
  49. includes=[TIME_H, 'sys/timeb.h'],
  50. libraries=libraries
  51. )
  52. TIMEB = rffi_platform.Struct(STRUCT_TIMEB, [('time', rffi.INT),
  53. ('millitm', rffi.INT)])
  54. class CConfigForClockGetTime:
  55. _compilation_info_ = ExternalCompilationInfo(
  56. includes=['time.h'],
  57. libraries=libraries
  58. )
  59. TIMESPEC = rffi_platform.Struct('struct timespec', [('tv_sec', rffi.LONG),
  60. ('tv_nsec', rffi.LONG)])
  61. constant_names = ['RUSAGE_SELF', 'EINTR', 'CLOCK_PROCESS_CPUTIME_ID']
  62. for const in constant_names:
  63. setattr(CConfig, const, rffi_platform.DefinedConstantInteger(const))
  64. defs_names = ['GETTIMEOFDAY_NO_TZ']
  65. for const in defs_names:
  66. setattr(CConfig, const, rffi_platform.Defined(const))
  67. def decode_timeval(t):
  68. return (float(rffi.getintfield(t, 'c_tv_sec')) +
  69. float(rffi.getintfield(t, 'c_tv_usec')) * 0.000001)
  70. def external(name, args, result, compilation_info=eci, **kwds):
  71. return rffi.llexternal(name, args, result,
  72. compilation_info=compilation_info, **kwds)
  73. def replace_time_function(name):
  74. func = getattr(pytime, name, None)
  75. if func is None:
  76. return lambda f: f
  77. return register_replacement_for(
  78. func,
  79. sandboxed_name='ll_time.ll_time_%s' % name)
  80. config = rffi_platform.configure(CConfig)
  81. globals().update(config)
  82. # Note: time.time() is used by the framework GC during collect(),
  83. # which means that we have to be very careful about not allocating
  84. # GC memory here. This is the reason for the _nowrapper=True.
  85. if HAVE_GETTIMEOFDAY:
  86. if GETTIMEOFDAY_NO_TZ:
  87. c_gettimeofday = external('gettimeofday',
  88. [lltype.Ptr(TIMEVAL)], rffi.INT,
  89. _nowrapper=True, releasegil=False)
  90. else:
  91. c_gettimeofday = external('gettimeofday',
  92. [lltype.Ptr(TIMEVAL), rffi.VOIDP], rffi.INT,
  93. _nowrapper=True, releasegil=False)
  94. if HAVE_FTIME:
  95. globals().update(rffi_platform.configure(CConfigForFTime))
  96. c_ftime = external(FTIME, [lltype.Ptr(TIMEB)],
  97. lltype.Void,
  98. _nowrapper=True, releasegil=False)
  99. c_time = external('time', [rffi.VOIDP], rffi.TIME_T,
  100. _nowrapper=True, releasegil=False)
  101. @replace_time_function('time')
  102. def time():
  103. void = lltype.nullptr(rffi.VOIDP.TO)
  104. result = -1.0
  105. if HAVE_GETTIMEOFDAY:
  106. with lltype.scoped_alloc(TIMEVAL) as t:
  107. errcode = -1
  108. if GETTIMEOFDAY_NO_TZ:
  109. errcode = c_gettimeofday(t)
  110. else:
  111. errcode = c_gettimeofday(t, void)
  112. if rffi.cast(rffi.LONG, errcode) == 0:
  113. result = decode_timeval(t)
  114. if result != -1:
  115. return result
  116. else: # assume using ftime(3)
  117. with lltype.scoped_alloc(TIMEB) as t:
  118. c_ftime(t)
  119. result = (float(intmask(t.c_time)) +
  120. float(intmask(t.c_millitm)) * 0.001)
  121. return result
  122. return float(c_time(void))
  123. # _______________________________________________________________
  124. # time.clock()
  125. if _WIN32:
  126. # hacking to avoid LARGE_INTEGER which is a union...
  127. QueryPerformanceCounter = external(
  128. 'QueryPerformanceCounter', [rffi.CArrayPtr(lltype.SignedLongLong)],
  129. lltype.Void, releasegil=False)
  130. QueryPerformanceFrequency = external(
  131. 'QueryPerformanceFrequency', [rffi.CArrayPtr(lltype.SignedLongLong)],
  132. rffi.INT, releasegil=False)
  133. class State(object):
  134. divisor = 0.0
  135. counter_start = 0
  136. state = State()
  137. elif CLOCK_PROCESS_CPUTIME_ID is not None:
  138. # Linux and other POSIX systems with clock_gettime()
  139. globals().update(rffi_platform.configure(CConfigForClockGetTime))
  140. TIMESPEC = TIMESPEC
  141. CLOCK_PROCESS_CPUTIME_ID = CLOCK_PROCESS_CPUTIME_ID
  142. eci_with_lrt = eci.merge(ExternalCompilationInfo(libraries=['rt']))
  143. c_clock_gettime = external('clock_gettime',
  144. [lltype.Signed, lltype.Ptr(TIMESPEC)],
  145. rffi.INT, releasegil=False,
  146. compilation_info=eci_with_lrt)
  147. if need_rusage:
  148. RUSAGE = RUSAGE
  149. RUSAGE_SELF = RUSAGE_SELF or 0
  150. c_getrusage = external('getrusage',
  151. [rffi.INT, lltype.Ptr(RUSAGE)],
  152. rffi.INT,
  153. releasegil=False)
  154. def win_perf_counter():
  155. with lltype.scoped_alloc(rffi.CArray(rffi.lltype.SignedLongLong), 1) as a:
  156. if state.divisor == 0.0:
  157. QueryPerformanceCounter(a)
  158. state.counter_start = a[0]
  159. QueryPerformanceFrequency(a)
  160. state.divisor = float(a[0])
  161. QueryPerformanceCounter(a)
  162. diff = a[0] - state.counter_start
  163. return float(diff) / state.divisor
  164. @replace_time_function('clock')
  165. def clock():
  166. if _WIN32:
  167. return win_perf_counter()
  168. elif CLOCK_PROCESS_CPUTIME_ID is not None:
  169. with lltype.scoped_alloc(TIMESPEC) as a:
  170. c_clock_gettime(CLOCK_PROCESS_CPUTIME_ID, a)
  171. result = (float(rffi.getintfield(a, 'c_tv_sec')) +
  172. float(rffi.getintfield(a, 'c_tv_nsec')) * 0.000000001)
  173. return result
  174. else:
  175. with lltype.scoped_alloc(RUSAGE) as a:
  176. c_getrusage(RUSAGE_SELF, a)
  177. result = (decode_timeval(a.c_ru_utime) +
  178. decode_timeval(a.c_ru_stime))
  179. return result
  180. # _______________________________________________________________
  181. # time.sleep()
  182. if _WIN32:
  183. Sleep = external('Sleep', [rffi.ULONG], lltype.Void)
  184. else:
  185. c_select = external('select', [rffi.INT, rffi.VOIDP,
  186. rffi.VOIDP, rffi.VOIDP,
  187. lltype.Ptr(TIMEVAL)], rffi.INT,
  188. save_err=rffi.RFFI_SAVE_ERRNO)
  189. @replace_time_function('sleep')
  190. def sleep(secs):
  191. if _WIN32:
  192. millisecs = secs * 1000.0
  193. while millisecs > UINT_MAX:
  194. Sleep(UINT_MAX)
  195. millisecs -= UINT_MAX
  196. Sleep(rffi.cast(rffi.ULONG, int(millisecs)))
  197. else:
  198. void = lltype.nullptr(rffi.VOIDP.TO)
  199. with lltype.scoped_alloc(TIMEVAL) as t:
  200. frac = math.fmod(secs, 1.0)
  201. rffi.setintfield(t, 'c_tv_sec', int(secs))
  202. rffi.setintfield(t, 'c_tv_usec', int(frac*1000000.0))
  203. if rffi.cast(rffi.LONG, c_select(0, void, void, void, t)) != 0:
  204. errno = rposix.get_saved_errno()
  205. if errno != EINTR:
  206. raise OSError(errno, "Select failed")