📄 io-manager.sml
字号:
(* io-manager.sml * * COPYRIGHT (c) 1995 AT&T Bell Laboratories. * COPYRIGHT (c) 1989-1991 John H. Reppy * * This is a generic I/O manager for CML. It uses the OS.IO polling * mechanism. * NOTE: it currently does not work if more than one thread blocks on the same * descriptor. *)structure IOManager : sig type iodesc type poll_desc type poll_info val ioEvt : poll_desc -> poll_info Event.event val pollIO : unit -> unit val anyWaiting : unit -> bool end = struct structure R = RepTypes structure S = Scheduler type iodesc = OS.IO.iodesc type poll_desc = OS.IO.poll_desc type poll_info = OS.IO.poll_info type io_wait_item = { pd : poll_desc, tid : R.trans_id ref, cleanUp : unit -> unit, k : poll_info SMLofNJ.Cont.cont } val waiting = ref ([] : io_wait_item list) (* In some OSs (e.g., Linux) this may raise an EINTR error, even though * it is non-blocking. *) fun poll l = OS.IO.poll(l, SOME(Time.zeroTime)) handle _ => [] (* NOTE: as in the case of condition variables (see event.sml), we need to * do the cleanUp routine when we enable the ioEvt (instead of in the blockFn * continuation). *) fun ioEvt pd = let fun blockFn {transId, cleanUp, next} = let val pi = SMLofNJ.Cont.callcc (fn k => let val item = {pd=pd, tid=transId, cleanUp=cleanUp, k=k} in waiting := item :: !waiting; next(); raise Fail "impossible: ioEvt" end) in pi end fun pollFn () = (case (poll [pd]) of [pi] => R.ENABLED{prio= ~1, doFn=(fn () => (S.atomicEnd(); pi))} | _ => R.BLOCKED blockFn (* end case *)) in R.BEVT[pollFn] end(** NOTE: there should be an OS.IO.infoToIODesc **) fun sameDesc (pi, pd) = (OS.IO.pollToIODesc(OS.IO.infoToPollDesc pi) = OS.IO.pollToIODesc pd) (* Take an I/O waiting queue and return the cleaned queue along with the list * of poll descriptors in the remaining elements. *) fun clean wq = let fun cl ([] : io_wait_item list, pds, q) = (pds, q) | cl ({tid=ref R.CANCEL, ...} :: r, pds, wq) = cl (r, pds, wq) | cl ((item as {pd, ...}) :: r, pds, wq) = cl (r, pd::pds, item::wq) in cl (wq, [], []) end (* enqueue a thread that is polling on the ready queue. We have to do some * continuation hacking to pass the poll info to the thread. We also must * catch the case where the transaction has been canceled, since a single * thread might be polling on multiple descriptors. *) fun enqueue ({tid as ref(R.TRANS id), cleanUp, k, pd}, pi) = let val uk = SMLofNJ.Cont.callcc (fn kk => ( SMLofNJ.Cont.callcc (fn uk => SMLofNJ.Cont.throw kk uk); SMLofNJ.Cont.throw k pi)) in tid := R.CANCEL; cleanUp(); S.enqueueThread (id, uk) end | enqueue ({tid=ref R.CANCEL, ...}, _) = () fun pollIO () = (case clean(! waiting) of ([], _) => waiting := [] | (pds, wq) => (case (poll pds) of [] => waiting := List.rev wq | l => let fun filter ([], r, wq) = waiting := List.revAppend(r, wq) | filter (pi::pis, (item : io_wait_item)::r, wq) = if sameDesc(pi, #pd item) then (enqueue (item, pi); filter (pis, r, wq)) else filter (pi::pis, r, item::wq) in filter (l, wq, []) end (* end case *)) (* end case *)) fun anyWaiting () = (case !waiting of [] => false | _ => true) end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -