message.lisp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. ;; mach_msg function.
  2. (defcfun ("mach_msg" %mach-msg)
  3. err
  4. (msg :pointer)
  5. (option msg-option)
  6. (send-size msg-size)
  7. (rcv-size msg-size)
  8. (rcv-name port)
  9. (timeout msg-timeout)
  10. (notify port))
  11. ;; A type must implement this methods.
  12. (defgeneric msg-type-exists? (obj))
  13. (defgeneric msg-type-size (obj))
  14. (defgeneric msg-type-number (type))
  15. (defgeneric msg-type-data? (obj data))
  16. (defgeneric msg-type->msg-type (obj))
  17. (defgeneric msg-type-set-data! (type ptr data))
  18. (defgeneric msg-type-get-data (type ptr))
  19. ;; Generic methods for the base type.
  20. (define-method (msg-type-exists? obj)
  21. #nil)
  22. (define-method (msg-type-size obj)
  23. 0)
  24. (define-method (msg-type-number type)
  25. 0)
  26. (define-method (msg-type-data? obj data)
  27. #nil)
  28. (define-method (msg-type->msg-type obj)
  29. #nil)
  30. (define-method (msg-type-set-data! type ptr data)
  31. #nil)
  32. (define-method (msg-type-get-data type ptr)
  33. #nil)
  34. (defmacro msg-add-type (type-name &key size number test
  35. msg-type
  36. set
  37. get)
  38. "Add a new message type with name 'type-name'.
  39. 'size' is an expression that calculates the type size.
  40. 'number' indicates the number of items in this type.
  41. 'test' a test to check if a specific object is inside the type domain.
  42. 'msg-type' indicates the message type code.
  43. 'set' is an expression that sets the memory area to this type.
  44. 'get' returns the object from a memory region.
  45. size * number gives bytes occupied by an object of this type.
  46. "
  47. #`(progn
  48. (defmethod msg-type-exists? ((obj (eql #,type-name))) #t)
  49. (defmethod msg-type-size ((obj (eql #,type-name))) #,size)
  50. (defmethod msg-type-number ((obj (eql #,type-name))) #,number)
  51. (defmethod msg-type-data? ((obj (eql #,type-name)) data) #,test)
  52. (defmethod msg-type->msg-type ((obj (eql #,type-name))) #,msg-type)
  53. (defmethod msg-type-set-data! ((type (eql #,type-name)) ptr data) #,set)
  54. (defmethod msg-type-get-data ((type (eql #,type-name)) ptr) #,get)))
  55. (msg-add-type :integer
  56. :size (foreign-type-size :integer)
  57. :number 1
  58. :test (exact-integer? data)
  59. :msg-type :type-integer-32
  60. :set (set! (mem-ref ptr :int) data)
  61. :get (mem-ref ptr :int))
  62. (msg-add-type :char
  63. :size 4
  64. :number 1
  65. :test (characterp data)
  66. :msg-type :type-char
  67. :set (set! (mem-ref ptr :char) (char-code data))
  68. :get (code-char (mem-ref ptr :char)))
  69. (msg-add-type :real
  70. :size (foreign-type-size :double)
  71. :number 1
  72. :test (exact-integer? data)
  73. :msg-type :type-integer-64
  74. :set (set! (mem-ref ptr :float) data)
  75. :get (mem-ref ptr :float))
  76. (msg-add-type :string
  77. :size 1
  78. :number 1024
  79. ;; XXX verify byte / unicode / something else
  80. ;; (length)
  81. :test (and (string? data) (<= (length data) 1024))
  82. :msg-type :type-string
  83. :set (lisp-string-to-foreign data
  84. ptr
  85. (1+ (length data)))
  86. :get (foreign-string-to-lisp ptr))
  87. (defclass <message-spec> ()
  88. ((fields :initform nil
  89. :accessor spec-fields
  90. :initarg :fields
  91. :documentation "List of types of this spec.")
  92. (id :initform 0
  93. :accessor spec-id
  94. :initarg :id
  95. :documentation "Message spec id, if any.")
  96. (size :initform 0
  97. :initarg #:size
  98. :accessor spec-size
  99. :documentation "Total size of messages of this kind."))
  100. :documentation "A spec is a combination of types that generates new kind of messages.")
  101. (define* (make-message-spec #:key fields (id 0))
  102. "Create a new message spec."
  103. (assert (fixnum? id))
  104. (assert (pair? fields))
  105. (validate-types fields)
  106. (make-instance <message-spec>
  107. #:fields fields
  108. #:size (calculate-size-total fields)
  109. #:id id))
  110. (define-class <message> ()
  111. (spec #:init-form #nil
  112. #:accessor msg-spec
  113. #:init-keyword #:spec
  114. #;(#:documentation "Spec of this message."))
  115. (ptr #:init-form #nil
  116. #:accessor ptr
  117. #:init-keyword #:ptr
  118. #;(#:documentation "Foreign pointer to a message structure."))
  119. #;(#:documentation "The message object, with a spec and a memory structure."))
  120. (define-method (msg-size (msg <message>))
  121. (spec-size (msg-spec msg)))
  122. (define-method (msg-fields (msg <message>))
  123. (spec-fields (msg-spec msg)))
  124. (define (msg-type-total-size field)
  125. "Returns the size of a message type."
  126. (* (msg-type-size field)
  127. (msg-type-number field)))
  128. (define (calculate-size-total fields)
  129. "Returns the size of a type field."
  130. (+ +msg-header-size+
  131. (loop* ((field #:in fields))
  132. #:sum (+ +msg-type-size+
  133. (msg-type-total-size field)))))
  134. (define (validate-types fields)
  135. "Validates the existence of the list of types 'fields'."
  136. (loop* ((field #:in fields))
  137. #:do (unless (msg-type-exists? field)
  138. (error "Type ~s not recognized" field))))
  139. (define* (make-message #:key spec (ptr #nil))
  140. "Creates a new message with the given spec and possibly a memory pointer."
  141. (let ((ptr-null? (null? ptr)))
  142. (when ptr-null?
  143. (set! ptr (foreign-alloc :char :count (spec-size spec))))
  144. (let ((obj (make-instance <message>
  145. #:spec spec
  146. #:ptr ptr)))
  147. (when ptr-null?
  148. (tg:finalize obj (lambda () (foreign-free ptr))))
  149. obj)))
  150. (define (validate-data fields datas)
  151. "Checks if the 'data' list only contains types specified in the 'fields' list."
  152. (assert (= (length fields) (length datas)))
  153. (loop* ((field #:in fields)
  154. (data #:in datas))
  155. #:do
  156. (unless (msg-type-data? field data)
  157. (error "Data ~s is not of type ~s"
  158. data field))))
  159. (define (msg-type-bits type)
  160. "Returns number of bits in a message type."
  161. (num-bits (msg-type-size type)))
  162. (define (has-timeout? timeout)
  163. "Check if 'timeout' is really a timeout value."
  164. (and (not (null? timeout))
  165. (number? timeout)
  166. (> timeout 0)))
  167. (define (fill-msg-header ptr size local remote id)
  168. "Fill the foreign pointer 'ptr' with size 'size', and ports 'remote' and 'local'. The message id is 'id'."
  169. (header-set-bits! ptr
  170. (if local
  171. (msgh-bits #:make-send #:make-send-once)
  172. (msgh-bits-remote #:make-send)))
  173. (header-set-size! ptr size)
  174. (header-set-local-port! ptr local)
  175. (header-set-remote-por!t ptr remote)
  176. (when id
  177. (header-set-id! ptr id)))
  178. (define (build-msg-type-val field)
  179. "Create a message val field, specifying a type."
  180. (let ((val 0))
  181. (set! val
  182. (set-type-name! val (msg-type->msg-type field)))
  183. (set! val
  184. (set-type-size! val (msg-type-bits field)))
  185. (set! val
  186. (set-type-number! val (msg-type-number field)))
  187. (set! val
  188. (set-type-inline! val t))
  189. (set! val
  190. (set-type-longform! val #nil))
  191. (set! val
  192. (set-type-deallocate! val #nil))
  193. val))
  194. (defmethod send-message ((msg <message>) &key (local #nil) remote data
  195. (timeout #nil) (notify #nil))
  196. "Sends the message 'msg' with data 'data' to 'remote' with source 'local'.
  197. A timeout value may be specified. A notification port 'notify' can also be passed."
  198. (validate-data (msg-fields msg) data)
  199. (let ((ptr (ptr msg))
  200. (size (msg-size msg))
  201. (fields (msg-fields msg)))
  202. (fill-msg-header ptr size local remote (spec-id (msg-spec msg)))
  203. (incf-pointer ptr +msg-header-size+)
  204. (loop* ((field #:in fields)
  205. (data-field #:in data))
  206. #:do
  207. (set! (mem-ref ptr 'msg-type)
  208. (build-msg-type-val field))
  209. (incf-pointer ptr +msg-type-size+)
  210. (msg-type-set-data! field ptr data-field)
  211. (incf-pointer ptr (msg-type-total-size field)))
  212. (let* ((timeout? (has-timeout? timeout)))
  213. (%mach-msg (ptr msg)
  214. (if timeout?
  215. '(:send-msg :send-timeout)
  216. '(:send-msg))
  217. size
  218. 0
  219. #nil
  220. (if timeout? timeout 0)
  221. notify))))
  222. (defmethod receive-message ((msg <message>) &key source (timeout #nil) (notify #nil))
  223. "Receives a message in port 'source' to 'msg' with a specific timeout (or none with #nil), with the notification port 'notify'."
  224. (let ((timeout? (has-timeout? timeout)))
  225. (when (eq? #t
  226. (%mach-msg (ptr msg)
  227. (if timeout?
  228. '(:rcv-msg :rcv-timeout)
  229. '(:rcv-msg))
  230. 0
  231. (msg-size msg)
  232. source
  233. (if timeout? timeout 0)
  234. notify))
  235. (validate-message msg))))
  236. (define-method (validate-message (msg <message>))
  237. "Validates the message present in the foreign pointer."
  238. (unless (eq? (msg-size msg) (header-get-size (ptr msg)))
  239. (return-from validate-message #nil))
  240. (when (spec-id (msg-spec msg))
  241. (unless (eq? (spec-id (msg-spec msg)) (get-message-id msg))
  242. (return-from validate-message #nil)))
  243. (let ((ptr (inc-pointer (ptr msg)
  244. +msg-header-size+)))
  245. (loop* ((field #:in (msg-fields msg)))
  246. #:do
  247. (let ((type-val (mem-ref ptr 'msg-type)))
  248. (unless (eq? (get-type-name type-val)
  249. (msg-type-to-msg-type field))
  250. (return-from validate-message #nil))
  251. (unless (eq? (get-type-size type-val)
  252. (msg-type-bits field))
  253. (warn "failed eval type size")
  254. (return-from validate-message #nil))
  255. (unless (eq? (get-type-number type-val)
  256. (msg-type-number field))
  257. (warn "failed eval type number")
  258. (return-from validate-message #nil))
  259. ; Jump to next message field.
  260. (incf-pointer ptr +msg-type-size+)
  261. (incf-pointer ptr (msg-type-total-size field))))
  262. t))
  263. (define-method (get-message (msg <message>))
  264. "Returns the message data in 'msg' as a list of objects."
  265. (let ((ptr (inc-pointer (ptr msg)
  266. +msg-header-size+)))
  267. (define (???-per-field field)
  268. (begin
  269. (incf-pointer ptr +msg-type-size+)
  270. (with-cleanup (incf-pointer ptr (msg-type-total-size field))
  271. (msg-type-get-data field ptr))))
  272. (map-in-order ???-per-field (msg-fields msg))))
  273. (define-method (get-message-id (msg <message>))
  274. "Returns the message id of this message."
  275. (header-get-id (ptr msg)))
  276. (define-method (get-message-local-port (msg <message>))
  277. "Returns the local port of this message."
  278. (header-get-local-port (ptr msg)))
  279. (define-method (get-message-remote-port (msg <message>))
  280. "Returns the remote port of this message."
  281. (header-get-remote-port (ptr msg)))
  282. ; Example code:
  283. ; (defvar *p1* (port-allocate :right-receive))
  284. ; (defvar *spec-mixed* (make-message-spec
  285. ; :fields '(:string :integer :char :string :integer :real)))
  286. ; (defvar *msg-mixed* (make-message :spec *spec-mixed*))
  287. ; (send-message *msg-mixed* :remote *p1* :data (list "abc" 42 #\b "cba" 314 3.14))
  288. ; (receive-message *msg-mixed* :source *p1*)
  289. ; (get-message *msg-mixed*) -> '("abc" 42 #\b "cba" 314 3.14)