# #-- # Copyright (c) 2006-2007, John Mettraux, OpenWFE.org # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # . Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # . Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # . Neither the name of the "OpenWFE" nor the names of its contributors may be # used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. #++ # # $Id: definitions.rb 2725 2006-06-02 13:26:32Z jmettraux $ # # # "made in Japan" # # John Mettraux at openwfe.org # require 'openwfe/utils' require 'openwfe/rudefinitions' require 'openwfe/expressions/flowexpression' # # base expressions like 'sequence' and 'concurrence' # module OpenWFE class ConcurrenceExpression < SequenceExpression attr_accessor \ :sync_expression def apply (workitem) sync = lookup_attribute(:sync, workitem, :generic) @sync_expression = \ get_expression_map().get_sync_class(sync).new(self, workitem) @children.each do |child| @sync_expression.add_child(child) end store_itself() concurrence = self @children.each do |child| Thread.new do begin concurrence.synchronize do get_expression_pool().apply(child, workitem.dup) end rescue Exception => e lwarn do "apply() " + "caught exception in concurrent child " + child.to_debug_s + "\n" + OpenWFE::exception_to_s(e) end end end end #@sync_expression.ready(self) # # this is insufficient, have to do that : synchronize do # # Making sure the freshest version of the concurrence # expression is used. # This is especially important when using pure persistence. # reloaded_self, _fei = get_expression_pool.fetch(@fei) reloaded_self.sync_expression.ready(reloaded_self) end end def reply (workitem) @sync_expression.reply(self, workitem) end end # # A base for sync expressions, currently empty. # That may change. # class SyncExpression def initialize() super() end end # # The classical OpenWFE sync expression. # Used by 'concurrence' and 'concurrent-iterator' # class GenericSyncExpression < SyncExpression attr_accessor \ :remaining_children, :count, :reply_count, :cancel_remaining, :unready_queue def initialize (synchable, workitem) super() @remaining_children = [] @reply_count = 0 @count = determine_count(synchable, workitem) @cancel_remaining = cancel_remaining?(synchable, workitem) @unready_queue = [] end # # when all the children got applied concurrently, the concurrence # calls this method to notify the sync expression that replies # can be processed # def ready (synchable) synchable.synchronize do synchable.ldebug do "ready() called by #{synchable.fei.to_debug_s} " + "#{@unready_queue.length} wi waiting" end queue = @unready_queue @unready_queue = nil synchable.store_itself() queue.each do |workitem| do_reply(synchable, workitem) end end end def add_child (child) @remaining_children << child end def reply (synchable, workitem) synchable.synchronize do if @unready_queue @unready_queue << workitem synchable.store_itself() synchable.ldebug do "#{self.class}.reply() "+ "#{@unready_queue.length} wi waiting..." end else do_reply(synchable, workitem) end end end protected def do_reply (synchable, workitem) synchable.ldebug do "#{self.class}.do_reply() "+ "#{workitem.last_expression_id.to_debug_s}" end @reply_count = @reply_count + 1 @remaining_children.delete(workitem.last_expression_id) if @remaining_children.length <= 0 synchable.reply_to_parent(workitem) return end if @count > 0 and @reply_count >= @count treat_remaining_children(synchable) synchable.reply_to_parent(workitem) return end synchable.store_itself() #synchable.ldebug do # "#{self.class}.do_reply() not replying to parent "+ # "#{workitem.last_expression_id.to_debug_s}" #end end def treat_remaining_children (synchable) expool = synchable.get_expression_pool @remaining_children.each do |child| synchable.ldebug do "#{self.class}.treat_remainining_children() " + "#{child.to_debug_s} " + "(cancel ? #{@cancel_remaining})" end if @cancel_remaining expool.cancel(child) else #expool.remove(child) expool.forget(child) end end end def cancel_remaining? (synchable_expression, workitem) s = synchable_expression.lookup_attribute( :remaining, workitem, :cancel) return s == :cancel.to_s end def determine_count (synchable_expression, workitem) s = synchable_expression.lookup_attribute(:count, workitem) return -1 if not s i = s.to_i return -1 if i < 1 return i end end # # Merges a workitem (source) into another (target). # If inPlace is left to false, a brand new workitem is returned, # else the merge occurs directly into the target workitem. # def merge (wiTarget, wiSource, inPlace=false) wiTarget = wiTarget.dup if not inPlace wiSource.attributes.each do | k, v | wiTarget.attributes[k.dup] = v.dup end end end