← Архив: Common Lisp

Как портабельно заснуть на наносекунды?

Author: · 03.10.2010 23:06
· original author: lithp
Добрый день,
Как портабельно заснуть на наносекунды? Пытался написать CFFI обертку над nanosleep(), но выяснилось, что __time_t тип зависит от платформы. Tак и придется писать для каждой?
· original author: 1349
usleep(ns / 1000);
· original author: lithp
usleep(ns / 1000);
это понятно :)
но на винде usleep()'a нет
· original author: LinkFly
Ой, есть же ещё винда. Я последнее время начал об этом забывать ;)
· original author: Love5an
не проверял, но, по-моему, оверхед на вызов нативной функции всяко превысит желаемый интервал в наносекундах
· original author: 1349
Несомненно. Спать имеет смысл где-то от десятков-сотни us.
· original author: lithp
Несомненно. Спать имеет смысл где-то от десятков-сотни us.

это да, хотелось спать в микросекундах, но так как usleep() не везде доступен, a
nanosleep() не везде одинаков, поэтому возник этот вопрос :)
· original author: treep
Могу ошибаться, но
(defun usleep (seconds micro-seconds)
  (declare (type (unsigned-byte 32) seconds)
           (type (integer 0 1000000) micro-seconds)
)

  (sleep (+ seconds (/ micro-seconds 1000000)))
)

(define-compiler-macro usleep (seconds micro-seconds)
  (if (zerop seconds)
      `(sleep (/ ,micro-seconds 1000000))
      `(usleep ,seconds ,micro-seconds)
)
)

(defun nanosleep (seconds nano-seconds)
  (declare (type (unsigned-byte 32) seconds)
           (type (integer 0 1000000000) nano-seconds)
)

  (sleep (+ seconds (/ nano-seconds 1000000000)))
)

(define-compiler-macro nanosleep (seconds nano-seconds)
  (if (zerop seconds)
      `(sleep (/ ,nano-seconds 1000000000))
      `(nano-sleep ,seconds ,nano-seconds)
)
)

Нужно смотреть для конкретной реализации - какое разрешение у sleеp, но по стандарту она принимает real, так что какое-то разрешение есть.
· original author: treep
Ещё sb-unix:nanosleep - для SBCL не на винде.
· original author: treep
Ещё
(defpackage #:nanosleep
  (:use     #:common-lisp
            #:cffi
)

  (:export  #:nanosleep)
)

(in-package #:nanosleep)
#+win32
(progn
  (define-foreign-library cygwin1
    (t (:default "cygwin1"))
)

  (use-foreign-library cygwin1)
)

(defctype time-t
  #-(or openbsd netbsd) :long
  #+(or openbsd netbsd) :int
)

(defcstruct timespec
  (tv-sec  time-t)
  (tv-nsec :long)
)

(declaim (inline %nanosleep))
(defcfun ("nanosleep" %nanosleep) :int
  (interval  :pointer)
  (remainder :pointer)
)

(defun nanosleep (seconds nano-seconds)
  (declare (type (unsigned-byte 32) seconds)
           (type (integer 0 1000000000) nano-seconds)
)

  (with-foreign-objects ((interval  'timespec)
                         (remainder 'timespec)
)

    (setf (foreign-slot-value interval  'timespec 'tv-sec)  seconds
          (foreign-slot-value interval  'timespec 'tv-nsec) nano-seconds
)

    (%nanosleep interval remainder)
)
)

Работает на Linux (и наверно на других юниксах) но не работает на win32 - видимо тягать cygwin1.dll всё таки не так надёжно :)
· original author: treep
И sb-win32:millisleep на винде, в итоге sleep просто готовит аргументы из real и вызывает sb-unix:nanosleep или sb-win32:millisleep в зависимости от платформы.
· original author: treep
А ещё (будет самое лучшее разрешение) можно засыпать на n-ое количество nop-ов
(sb-c:defknown %%nop () t)
(sb-c:define-vop (%%nop)
  (:translate %%nop)
  (:policy :fast-safe)
  (:generator 0 (sb-c::inst nop))
)

(declaim (inline %nop))
(defun %nop () (%%nop))
(defmacro nop (count)
  `(progn
     ,@(loop :repeat count
             :collect '(%nop)
)
)
)

CL-USER> (macroexpand '(nop 5))
(PROGN (%NOP) (%NOP) (%NOP) (%NOP) (%NOP))
(defun foo ()
  (nop 5)
)

(disassemble 'foo)
; disassembly for FOO
; 242DD50B:       E8C0A813DC       CALL #x417DD0              ; _check_for_gc_suspension
                                                             ; no-arg-parsing entry point
;       10:       90               NOP
;       11:       90               NOP
;       12:       90               NOP
;       13:       90               NOP
;       14:       90               NOP
;       15:       BA0B001022       MOV EDX, 571473931
;       1A:       8BE5             MOV ESP, EBP
;       1C:       F8               CLC
;       1D:       5D               POP EBP
;       1E:       C3               RET
;       1F:       CC0A             BREAK 10                   ; error trap
;       21:       02               BYTE #X02
;       22:       18               BYTE #X18                  ; INVALID-ARG-COUNT-ERROR
;       23:       4F               BYTE #X4F                  ; ECX
· original author: treep
Хотя нет, от этого образ будет разбухать :)
· original author: lithp
> Работает на Linux (и наверно на других юниксах) но не работает на win32 - видимо тягать cygwin1.dll всё таки не так надёжно :)
не работает на винде, потому что time_t другой размерности :) - 64 бита, и nanosleep() нативный тоже есть
так и придется оборачивать с учетом платформы
· original author: treep
А обычный sleep с дробным аргументом не подойдёт? (nanosleep на юниксах и millisleep на винде)
· original author: lithp
нужны микросекунды. Потом, не понятно, с какой точностью CL'льный sleep работает на других реализациях
· original author: Ander Skirnir
А еще можно на нное кол-во факториалов засыпать :)
· original author: treep
Ага, "как заснуть с помощью холостого цикла" :) Как я слышал nop-ами спят в разных там сопрограммах в микроконтроллерах (явно не наш случай).
· original author: treep
Тот или иной *sleep хотя бы даёт что-то понять ядру.
· original author: treep
А не получится добавить там в defctype size-t #+win32 случай? Чему он там равен. Вообще на видне вроде тольо millisleep, в какой дин. библиотеки может (и есть ли вообще) лежать nanosleep - не известно. В cygwin1.dll есть, но что-то не работает (просто не возвращает управления после вызова).
> Потом, не понятно, с какой точностью CL'льный sleep работает на других реализациях
Я думаю что везде так - nanosleep на юниксах и millisleep на винде, потому что никого другого способа его реализации в голову не приходит (не игнорировать же дробные аргументы меньшие единицы).
· original author: lithp
Прогнал я с nanosleep'ом под винду, пошукал MSDN, его там нет :/
· original author: treep
Короче, гугл говорит, что люди говорят, что в винде нет ничего лучше millisleep в user-mode ;)
· original author: treep
Ещё одна попытка:
;;;;
;;;; sleep.lisp - portable *sleep functions.
;;;;
;;;;   Note: require `cygwin1.dll' on win32 platform.
;;;;

(defpackage #:sleep
  (:use     #:common-lisp
            #:cffi
)

  (:export  #:usleep
            #:nanosleep
            #:millisleep
)
)

(in-package #:sleep)
;;;; using `cygwin1.dll' on win32 platform
#+win32
(progn
  (define-foreign-library cygwin1
    (t (:default "cygwin1"))
)

  (use-foreign-library cygwin1)
)

;;;; foreign types

;;; From the `usleep' man page:
;;;
;;;   The type useconds_t is an unsigned integer type capable of holding integers in the
;;;   range [0, 1000000]. Programs will be more portable if they never mention this type
;;;   explicitly. Use the `unsigned int' type instead of `unsigned long' or `useconds_t'.
;;;
(defctype portable-useconds-t :unsigned-int)
(defctype time-t
  #-(or openbsd netbsd) :long ;; FIXME: include cygwin/win32 (?)
 #+(or openbsd netbsd) :int
)

(defcstruct timespec
  (tv-sec  time-t)
  (tv-nsec :long)
)

;;;; foreign functions

(declaim (inline %usleep))
(defcfun ("usleep" %usleep) :void
  (usec portable-useconds-t)
)

(declaim (inline %nanosleep))
(defcfun ("nanosleep" %nanosleep) :int
  (interval  :pointer)
  (remainder :pointer)
)

#+win32
(progn
  (declaim (inline %millisleep))
  (defcfun ("Sleep@4" %millisleep) :void
    (milliseconds :unsigned-short)
)
)

;;;; funtions for export

(defun usleep (useconds)
  (declare (type (integer 0 1000000) useconds))
  (%usleep useconds)
  (values)
)

(defun nanosleep (seconds nano-seconds)
  (declare (type (unsigned-byte 32) seconds)
           (type (integer 0 1000000000) nano-seconds)
)

  (with-foreign-objects ((interval  'timespec)
                         (remainder 'timespec)
)

    (setf (foreign-slot-value interval 'timespec 'tv-sec)  seconds
          (foreign-slot-value interval 'timespec 'tv-nsec) nano-seconds
)

    (%nanosleep interval remainder)
)
)

(defun millisleep (milliseconds)
  (declare (type (integer 0 1000) milliseconds))
  #-win32
  (%usleep (* milliseconds 1000))
  #+win32
  (%millisleep milliseconds)
  (values)
)

В итоге на винде работает только родной millisleep (который sleep в winapi), а цыгвин рушит sbcl:
(load "sleep.lisp")
(sleep:millisleep 1)
;; No values

(sleep:usleep 1)
fatal error encountered in SBCL pid 1304(tid 0x002C1558):
gc signals blocked
Welcome to LDB, a low-level debugger for the Lisp runtime environment.
ldb>
(load "sleep.lisp")
(sleep:nanosleep 0 1)
fatal error encountered in SBCL pid 1536(tid 0x003D1558):
gc signals blocked
Welcome to LDB, a low-level debugger for the Lisp runtime environment.
ldb>
· original author: treep
У них (в cygwin) в рассылке писалось что usleep вызывает nanosleep, а nanosleep лежит в sygnal.c - вот видимо он сигналами что-то там и рушит (хотя какие сигналы на винде?).
· original author: treep
У них (в cygwin) в рассылке писалось что usleep вызывает nanosleep, а nanosleep лежит в sgnal.c - вот видимо он сигналами что-то там и рушит (хотя какие сигналы на винде?).