(* Lwt * http://www.ocsigen.org * Copyright (C) 2008 Jérôme Vouillon * Laboratoire PPS - CNRS Université Paris Diderot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, with linking exceptions; * either version 2.1 of the License, or (at your option) any later version. * See COPYING file for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) open Lwt (* XXX Close after some timeout ... *) type 'a t = { create : unit -> 'a Lwt.t; check : 'a -> (bool -> unit) -> unit; max : int; mutable count : int; list : 'a Queue.t; waiters : 'a Lwt.u Lwt_sequence.t } let create m ?(check = fun _ f -> f true) create = { max = m; create = create; check = check; count = 0; list = Queue.create (); waiters = Lwt_sequence.create () } let create_member p = try_lwt p.count <- p.count + 1; (* must be done before p.create *) lwt mem = p.create () in return mem with exn -> (* create failed, so don't increment count *) p.count <- p.count - 1; raise_lwt exn let acquire p = try return (Queue.take p.list) with Queue.Empty -> if p.count < p.max then create_member p else begin let waiter, wakener = task () in let node = Lwt_sequence.add_r wakener p.waiters in on_cancel waiter (fun () -> Lwt_sequence.remove node); waiter end let release p c = try wakeup_later (Lwt_sequence.take_l p.waiters) c with Lwt_sequence.Empty -> Queue.push c p.list let checked_release p c = p.check c begin fun ok -> if ok then release p c else ignore (p.count <- p.count - 1; lwt c = create_member p in release p c; return ()) end let use p f = lwt c = acquire p in try_lwt lwt r = f c in release p c; return r with e -> checked_release p c; raise_lwt e