#!/usr/local/bin/ruby # TupleSpace # $Id: tuplespace.rb,v 1.5 2000/07/09 16:46:08 mas Exp $ # $Author: mas $ # Copyright (c) 1999-2000 Masatoshi SEKI # You can redistribute it and/or modify it under the same term as Ruby. require 'thread' class TupleSpace class Template def initialize(list) @list = list @check_idx = [] @list.each_with_index do |x, i| @check_idx.push i if x end @size = @list.size end attr :size alias length size def match(tuple) return nil if tuple.size != self.size @check_idx.each do |i| unless @list[i] === tuple[i] return false end end return true end end def initialize @que = {} @waiting = {} @que.taint # enable tainted comunication @waiting.taint self.taint end def wakeup_waiting(tuple) sz = tuple.length return nil unless @waiting[sz] x = nil i = -1 found = false @waiting[sz] = @waiting[sz].find_all { |x| if x[0].match(tuple) begin x[1].wakeup rescue ThreadError end false else true end } end def put_waiting(template, thread) sz = template.length @waiting[sz] = [] unless @waiting[sz] @waiting[sz].push([Template.new(template), thread]) end private :wakeup_waiting private :put_waiting def get_que(template) sz = template.length return nil unless @que[sz] template = Template.new(template) x = nil i = -1 found = false @que[sz].each_with_index do |x, i| if template.match(x) found = true break end end return nil unless found @que[sz].delete_at(i) return x end def put_que(tuple) sz = tuple.length @que[sz] = [] unless @que[sz] @que[sz].push tuple end private :get_que private :put_que def out(*tuples) tuples.each do |tuple| Thread.critical = true put_que(tuple) wakeup_waiting(tuple) Thread.critical = false end end alias put out def in(template, non_block=false) begin loop do Thread.critical = true tuple = get_que(template) unless tuple if non_block raise ThreadError, "queue empty" end put_waiting(template, Thread.current) Thread.stop else return tuple end end ensure Thread.critical = false end end alias get in def rd(template, non_block=false) tuple = self.in(template, non_block) out(tuple) tuple end end if __FILE__ == $0 ts = TupleSpace.new clients = [] servers = [] def server(ts, id) Thread.start { loop do req = ts.in(['req', nil, nil]) ac = req[1] num = req[2] sleep id ts.out([ac, id, num, num * num]) end } end def client(ts, n) Thread.start { ac = Object.new tuples = (1..10).collect { |i| ['req', ac, i * 10 + n] } ts.out(*tuples) ts.out(tuples[0]) puts "out: #{n}" 11.times do |i| ans = ts.in([ac, nil, nil, nil]) puts "client(#{n}) server(#{ans[1]}) #{ans[2]} #{ans[3]}" end } end def watcher(ts) Thread.start { loop do begin sleep 1 p ts.rd(['req', nil, nil], true) rescue ThreadError puts "'req' not found." end end } end (0..3).each do |n| servers.push(server(ts, n)) end (1..6).each do |n| clients.push(client(ts, n)) end (1..3).each do watcher(ts) end clients.each do |t| t.join end end