Please disable Adblockers and enable JavaScript for domain CEWebS.cs.univie.ac.at! We have NO ADS, but they may interfere with some of our course material.

Name: lib/weel.rb 
1:
# encoding: utf-8
2:
#
3:
# This file is part of WEEL.
4:
#
5:
# WEEL is free software: you can redistribute it and/or modify it under the terms
6:
# of the GNU General Public License as published by the Free Software Foundation,
7:
# either version 3 of the License, or (at your option) any later version.
8:
#
9:
# WEEL is distributed in the hope that it will be useful, but WITHOUT ANY
10:
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11:
# PARTICULAR PURPOSE.  See the GNU General Public License for more details.
12:
#
13:
# You should have received a copy of the GNU General Public License along with
14:
# WEEL (file COPYING in the main directory).  If not, see
15:
# <http://www.gnu.org/licenses/>.
16:
 
17:
require 'thread'
18:
 
19:
# OMG!111! deep cloning for ReadHashes
20:
class Object # {{{
21:
  def deep_clone
22:
    return @deep_cloning_obj if @deep_cloning
23:
    @deep_cloning_obj = clone
24:
    @deep_cloning_obj.instance_variables.each do |var|
25:
      val = @deep_cloning_obj.instance_variable_get(var)
26:
      begin
27:
        @deep_cloning = true
28:
        val = val.deep_clone
29:
      rescue TypeError
30:
        next
31:
      ensure
32:
        @deep_cloning = false
33:
      end
34:
      @deep_cloning_obj.instance_variable_set(var, val)
35:
    end
36:
    deep_cloning_obj = @deep_cloning_obj
37:
    @deep_cloning_obj = nil
38:
    deep_cloning_obj
39:
  end
40:
end #}}}
41:
 
42:
class WEEL
43:
  def initialize(*args)# {{{
44:
    @dslr = DSLRealization.new
45:
    @dslr.__weel_handlerwrapper_args = args
46:
 
47:
    initialize_search if methods.include?(:initialize_search)
48:
    initialize_data if methods.include?(:initialize_data)
49:
    initialize_endpoints if methods.include?(:initialize_endpoints)
50:
    initialize_handlerwrapper if methods.include?(:initialize_handlerwrapper)
51:
    initialize_control if methods.include?(:initialize_control)
52:
  end # }}}
53:
 
54:
  module Signal # {{{
55:
    class Skip < Exception; end
56:
    class SkipManipulate < Exception; end
57:
    class StopSkipManipulate < Exception; end
58:
    class Stop < Exception; end
59:
    class Proceed < Exception; end
60:
    class NoLongerNecessary < Exception; end
61:
    class Again < Exception; end
62:
    class Error < Exception; end
63:
  end # }}}
64:
 
65:
  class ReadStructure # {{{
66:
    def initialize(data,endpoints)
67:
      @__weel_data = data
68:
      @__weel_endpoints = endpoints
69:
      @changed_data = []
70:
      @changed_endpoints = []
71:
    end
72:
 
73:
    def data
74:
      ReadHash.new(@__weel_data)
75:
    end
76:
    def endpoints
77:
      ReadHash.new(@__weel_endpoints)
78:
    end
79:
  end # }}}
80:
  class ManipulateStructure # {{{
81:
    def initialize(data,endpoints,status)
82:
      @__weel_data = data
83:
      @__weel_data_orig = @__weel_data.transform_values{|val| Marshal.dump(val) }
84:
      @__weel_endpoints = endpoints
85:
      @__weel_endpoints_orig = @__weel_endpoints.transform_values{|val| Marshal.dump(val) }
86:
      @__weel_status = status
87:
      @changed_status = "#{status.id}-#{status.message}"
88:
      @changed_data = []
89:
      @touched_data = []
90:
      @changed_endpoints = []
91:
      @touched_endpoints = []
92:
    end
93:
 
94:
    def changed_data
95:
      @touched_data.each do |e|
96:
        if Marshal.dump(@__weel_data[e]) != @__weel_data_orig[e]
97:
          @changed_data << e
98:
        end
99:
      end
100:
      @changed_data
101:
    end
102:
    def changed_endpoints
103:
      @changed_endpoints
104:
    end
105:
 
106:
    def original_data
107:
      @__weel_data_orig.transform_values{|val| Marshal.load(val) }
108:
    end
109:
 
110:
    def original_endpoints
111:
      @__weel_endpoints_orig.transform_values{|val| Marshal.load(val) }
112:
    end
113:
 
114:
    def changed_status
115:
      @changed_status != "#{status.id}-#{status.message}"
116:
    end
117:
 
118:
    def data
119:
      ManipulateHash.new(@__weel_data,@touched_data,@changed_data)
120:
    end
121:
    def endpoints
122:
      ManipulateHash.new(@__weel_endpoints,@touched_endpoints,@changed_endpoints)
123:
    end
124:
    def status
125:
      @__weel_status
126:
    end
127:
  end # }}}
128:
  class ManipulateHash # {{{
129:
    attr_reader :__weel_touched, :__weel_changed
130:
 
131:
    def initialize(values,touched,changed)
132:
      @__weel_values = values
133:
      @__weel_touched = touched
134:
      @__weel_changed = changed
135:
    end
136:
 
137:
    def delete(value)
138:
      if @__weel_values.key?(value)
139:
        @__weel_changed << value
140:
        @__weel_values.delete(value)
141:
      end
142:
    end
143:
 
144:
    def clear
145:
      @__weel_changed += @__weel_values.keys
146:
      @__weel_values.clear
147:
    end
148:
 
149:
    def method_missing(name,*args)
150:
      if args.empty? && @__weel_values.key?(name)
151:
        @__weel_touched << name
152:
        @__weel_values[name]
153:
      elsif name.to_s[-1..-1] == "=" && args.length == 1
154:
        temp = name.to_s[0..-2]
155:
        @__weel_changed << temp.to_sym
156:
        @__weel_values[temp.to_sym] = args[0]
157:
      elsif name.to_s == "[]=" && args.length == 2
158:
        @__weel_changed << args[0]
159:
        @__weel_values[args[0]] = args[1]
160:
      elsif name.to_s == "[]" && args.length == 1
161:
        @__weel_touched << args[0]
162:
        @__weel_values[args[0]]
163:
      else
164:
        nil
165:
      end
166:
    end
167:
  end # }}}
168:
 
169:
  class Status # {{{
170:
    def initialize(id,message)
171:
      @id      = id
172:
      @message = message
173:
      @nudge   = Queue.new
174:
    end
175:
    def update(id,message)
176:
      @id      = id
177:
      @message = message
178:
    end
179:
    def nudge!
180:
      @nudge.clear
181:
      @nudge.push(nil)
182:
    end
183:
    def wait_until_nudged!
184:
      @nudge.pop
185:
    end
186:
    attr_reader :id, :message
187:
  end #}}}
188:
 
189:
  class ReadHash # {{{
190:
    def initialize(values,sim=false)
191:
      @__weel_values = values
192:
      @__weel_sim = sim
193:
    end
194:
 
195:
    def to_json(*args)
196:
      @__weel_values.to_json(*args)
197:
    end
198:
 
199:
    def method_missing(name,*args)
200:
      if args.empty? && @__weel_values.key?(name)
201:
        if @__weel_sim
202:
          "➤#{name}"
203:
        else
204:
          @__weel_values[name]
205:
        end
206:
        #TODO dont let user change stuff e.g. if return value is an array (deep clone and/or deep freeze it?)
207:
      else
208:
        nil
209:
      end
210:
    end
211:
  end # }}}
212:
 
213:
  class HandlerWrapperBase # {{{
214:
    def self::inform_state_change(arguments,newstate); end
215:
    def self::inform_syntax_error(arguments,err,code); end
216:
    def self::inform_handlerwrapper_error(arguments,err); end
217:
    def self::inform_position_change(arguments,ipc); end
218:
    def self::modify_position_details(arguments); end
219:
 
220:
    def initialize(arguments,endpoint=nil,position=nil,continue=nil); end
221:
 
222:
    def activity_handle(passthrough, parameters); end
223:
    def activity_manipulate_handle(parameters); end
224:
 
225:
    def activity_result_value; end
226:
 
227:
    def activity_stop; end
228:
    def activity_passthrough_value; end
229:
 
230:
    def activity_no_longer_necessary; end
231:
 
232:
    def inform_activity_done; end
233:
    def inform_activity_manipulate; end
234:
    def inform_activity_failed(err); end
235:
    def inform_manipulate_change(status,changed_data,changed_endpoints,data,endpoints); end
236:
 
237:
    def vote_sync_before(parameters=nil); true; end
238:
    def vote_sync_after; true; end
239:
 
240:
    # type       => activity, loop, parallel, choice
241:
    # nesting    => none, start, end
242:
    # eid        => id's also for control structures
243:
    # parameters => stuff given to the control structure
244:
    def simulate(type,nesting,sequence,parent,parameters={}); end
245:
 
246:
    def callback(result=nil,options={}); end
247:
 
248:
    def test_condition(mr,code); mr.instance_eval(code); end
249:
    def manipulate(mr,code,result=nil); mr.instance_eval(code); end
250:
  end  # }}}
251:
 
252:
  class Position # {{{
253:
    attr_reader :position
254:
    attr_accessor :detail, :passthrough
255:
    def initialize(position, detail=:at, passthrough=nil) # :at or :after or :unmark
256:
      @position = position
257:
      @detail = detail
258:
      @passthrough = passthrough
259:
    end
260:
    def as_json(*)
261:
      jsn = { 'position' => @position }
262:
      jsn['passtrough'] = @passthrough if @passthrough
263:
      jsn
264:
    end
265:
    def to_s
266:
      as_json.to_s
267:
    end
268:
    def to_json(*args)
269:
      as_json.to_json(*args)
270:
    end
271:
  end # }}}
272:
 
273:
   class Continue # {{{
274:
     def initialize
275:
       @q = Queue.new
276:
       @m = Mutex.new
277:
     end
278:
     def waiting?
279:
       @m.synchronize do
280:
         !@q.empty?
281:
       end
282:
     end
283:
     def continue(*args)
284:
       @q.push(args.length <= 1 ? args[0] : args)
285:
     end
286:
     def clear
287:
      @q.clear
288:
     end
289:
     def wait
290:
       @q.deq
291:
     end
292:
   end #}}}
293:
 
294:
  def self::search(weel_search)# {{{
295:
    define_method :initialize_search do
296:
      self.search weel_search
297:
    end
298:
  end # }}}
299:
  def self::endpoint(new_endpoints)# {{{
300:
    @@__weel_new_endpoints ||= {}
301:
    @@__weel_new_endpoints.merge! new_endpoints
302:
    remove_method :initialize_endpoints if method_defined? :initialize_endpoints
303:
    define_method :initialize_endpoints do
304:
      @@__weel_new_endpoints.each do |name,value|
305:
        @dslr.__weel_endpoints[name.to_s.to_sym] = value
306:
      end
307:
    end
308:
  end # }}}
309:
  def self::data(data_elements)# {{{
310:
    @@__weel_new_data_elements ||= {}
311:
    @@__weel_new_data_elements.merge! data_elements
312:
    define_method :initialize_data do
313:
      @@__weel_new_data_elements.each do |name,value|
314:
        @dslr.__weel_data[name.to_s.to_sym] = value
315:
      end
316:
    end
317:
  end # }}}
318:
  def self::handlerwrapper(aClassname, *args)# {{{
319:
    define_method :initialize_handlerwrapper do
320:
      self.handlerwrapper = aClassname
321:
      self.handlerwrapper_args = args unless args.empty?
322:
    end
323:
  end # }}}
324:
  def self::control(flow, &block)# {{{
325:
    @@__weel_control_block = block
326:
    define_method :initialize_control do
327:
      self.description = @@__weel_control_block
328:
    end
329:
  end #  }}}
330:
  def self::flow # {{{
331:
  end #}}}
332:
 
333:
  class DSLRealization # {{{
334:
    def  initialize #{{{
335:
      @__weel_search_positions = {}
336:
      @__weel_positions = Array.new
337:
      @__weel_main = nil
338:
      @__weel_data ||= Hash.new
339:
      @__weel_endpoints ||= Hash.new
340:
      @__weel_handlerwrapper = HandlerWrapperBase
341:
      @__weel_handlerwrapper_args = []
342:
      @__weel_state = :ready
343:
      @__weel_status = Status.new(0,"undefined")
344:
      @__weel_sim = -1
345:
    end #}}}
346:
    attr_accessor :__weel_search_positions, :__weel_positions, :__weel_main, :__weel_data, :__weel_endpoints, :__weel_handlerwrapper, :__weel_handlerwrapper_args
347:
    attr_reader :__weel_state, :__weel_status
348:
 
349:
    # DSL-Constructs for atomic calls to external services (calls) and pure context manipulations (manipulate).
350:
    # Calls can also manipulate context (after the invoking the external services)
351:
    # position: a unique identifier within the wf-description (may be used by the search to identify a starting point)
352:
    # endpoint: (only with :call) ep of the service
353:
    # parameters: (only with :call) service parameters
354:
    def call(position, endpoint, parameters: {}, finalize: nil, update: nil, &finalizeblk) #{{{
355:
      __weel_activity(position,:call,endpoint,parameters,finalize||finalizeblk,update)
356:
    end #}}}
357:
    # when two params, second param always script
358:
    # when block and two params, parameters stays
359:
    def manipulate(position, parameters=nil, script=nil, &scriptblk) #{{{
360:
      if scriptblk.nil? && script.nil? && !parameters.nil?
361:
        script, parameters = parameters, nil
362:
      end
363:
      __weel_activity(position,:manipulate,nil,parameters||{},script||scriptblk)
364:
    end #}}}
365:
 
366:
    # Parallel DSL-Construct
367:
    # Defines Workflow paths that can be executed parallel.
368:
    # May contain multiple branches (parallel_branch)
369:
    def parallel(type=nil)# {{{
370:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
371:
 
372:
      Thread.current[:branches] = []
373:
      Thread.current[:branch_finished_count] = 0
374:
      Thread.current[:branch_event] = Continue.new
375:
      Thread.current[:mutex] = Mutex.new
376:
 
377:
      hw, pos = __weel_sim_start(:parallel) if __weel_sim
378:
 
379:
      __weel_protect_yield(&Proc.new)
380:
 
381:
      Thread.current[:branch_wait_count] = (type.is_a?(Hash) && type.size == 1 && type[:wait] != nil && (type[:wait].is_a?(Integer) && type[:wait] > 0) ? type[:wait] : Thread.current[:branches].size)
382:
      1.upto Thread.current[:branches].size do
383:
        Thread.current[:branch_event].wait
384:
      end
385:
 
386:
      Thread.current[:branches].each do |thread|
387:
        # decide after executing block in parallel cause for coopis
388:
        # it goes out of search mode while dynamically counting branches
389:
        if Thread.current[:branch_search] == false
390:
          thread[:branch_search] = false
391:
        end
392:
        thread[:start_event].continue
393:
      end
394:
 
395:
      Thread.current[:branch_event].wait
396:
 
397:
      __weel_sim_stop(:parallel,hw,pos) if __weel_sim
398:
 
399:
      unless self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped
400:
        # first set all to no_longer_neccessary
401:
        Thread.current[:branches].each do |thread|
402:
          if thread.alive?
403:
            thread[:nolongernecessary] = true
404:
            __weel_recursive_continue(thread)
405:
          end
406:
        end
407:
        # wait for all
408:
        Thread.current[:branches].each do |thread|
409:
          __weel_recursive_join(thread)
410:
        end
411:
      end
412:
    end # }}}
413:
 
414:
    # Defines a branch of a parallel-Construct
415:
    def parallel_branch(*vars)# {{{
416:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
417:
      branch_parent = Thread.current
418:
 
419:
      if __weel_sim
420:
        # catch the potential execution in loops inside a parallel
421:
        current_branch_sim_pos = branch_parent[:branch_sim_pos]
422:
      end
423:
 
424:
      Thread.current[:branches] << Thread.new(*vars) do |*local|
425:
        Thread.current.abort_on_exception = true
426:
        Thread.current[:branch_status] = false
427:
        Thread.current[:branch_parent] = branch_parent
428:
        Thread.current[:start_event] = Continue.new
429:
 
430:
        if __weel_sim
431:
          Thread.current[:branch_sim_pos] = @__weel_sim += 1
432:
        end
433:
 
434:
        # parallel_branch could be possibly around an alternative. Thus thread has to inherit the alternative_executed
435:
        # after branching, update it in the parent (TODO)
436:
        if branch_parent[:alternative_executed] && branch_parent[:alternative_executed].length > 0
437:
          Thread.current[:alternative_executed] = [branch_parent[:alternative_executed].last]
438:
          Thread.current[:alternative_mode] = [branch_parent[:alternative_mode].last]
439:
        end
440:
        branch_parent[:branch_event].continue
441:
        Thread.current[:start_event].wait
442:
 
443:
        if __weel_sim
444:
          handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args
445:
          handlerwrapper.simulate(:parallel_branch,:start,Thread.current[:branch_sim_pos],current_branch_sim_pos)
446:
        end
447:
 
448:
        __weel_protect_yield(*local, &Proc.new)
449:
 
450:
        __weel_sim_stop(:parallel_branch,handlerwrapper,current_branch_sim_pos) if __weel_sim
451:
 
452:
        branch_parent[:mutex].synchronize do
453:
          Thread.current[:branch_status] = true
454:
          branch_parent[:branch_finished_count] += 1
455:
          if branch_parent[:branch_finished_count] == branch_parent[:branch_wait_count] && self.__weel_state != :stopping && self.__weel_state != :finishing
456:
            branch_parent[:branch_event].continue
457:
          end
458:
        end
459:
        if self.__weel_state != :stopping && self.__weel_state != :stopped && self.__weel_state != :finishing
460:
          if Thread.current[:branch_position]
461:
            @__weel_positions.delete Thread.current[:branch_position]
462:
            begin
463:
              ipc = {}
464:
              ipc[:unmark] = [Thread.current[:branch_position]]
465:
              @__weel_handlerwrapper::inform_position_change(@__weel_handlerwrapper_args,ipc)
466:
            end rescue nil
467:
            Thread.current[:branch_position] = nil
468:
          end
469:
        end
470:
      end
471:
    end # }}}
472:
 
473:
    # Choose DSL-Construct
474:
    # Defines a choice in the Workflow path.
475:
    # May contain multiple execution alternatives
476:
    def choose(mode=:inclusive) # {{{
477:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
478:
      Thread.current[:alternative_executed] ||= []
479:
      Thread.current[:alternative_mode] ||= []
480:
      Thread.current[:alternative_executed] << false
481:
      Thread.current[:alternative_mode] << mode
482:
      hw, pos = __weel_sim_start(:choose,:mode => Thread.current[:alternative_mode].last) if __weel_sim
483:
      __weel_protect_yield(&Proc.new)
484:
      __weel_sim_stop(:choose,hw,pos,:mode => Thread.current[:alternative_mode].last) if __weel_sim
485:
      Thread.current[:alternative_executed].pop
486:
      Thread.current[:alternative_mode].pop
487:
      nil
488:
    end # }}}
489:
 
490:
    # Defines a possible choice of a choose-Construct
491:
    # Block is executed if condition == true or
492:
    # searchmode is active (to find the starting position)
493:
    def alternative(condition,args={})# {{{
494:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
495:
      hw, pos = __weel_sim_start(:alternative,args.merge(:mode => Thread.current[:alternative_mode].last, :condition => ((condition.is_a?(String) || condition.is_a?(Proc)) ? condition : nil))) if __weel_sim
496:
      Thread.current[:mutex] ||= Mutex.new
497:
      Thread.current[:mutex].synchronize do
498:
        return if Thread.current[:alternative_mode][-1] == :exclusive && Thread.current[:alternative_executed][-1] == true
499:
        if (condition.is_a?(String) || condition.is_a?(Proc)) && !__weel_sim
500:
          condition = __weel_eval_condition(condition)
501:
        end
502:
        Thread.current[:alternative_executed][-1] = true if condition
503:
      end
504:
      __weel_protect_yield(&Proc.new) if __weel_is_in_search_mode || __weel_sim || condition
505:
      __weel_sim_stop(:alternative,hw,pos,args.merge(:mode => Thread.current[:alternative_mode].last, :condition => ((condition.is_a?(String) || condition.is_a?(Proc)) ? condition : nil))) if __weel_sim
506:
    end # }}}
507:
    def otherwise(args={}) # {{{
508:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
509:
      hw, pos = __weel_sim_start(:otherwise,args.merge(:mode => Thread.current[:alternative_mode].last)) if __weel_sim
510:
      __weel_protect_yield(&Proc.new) if __weel_is_in_search_mode || __weel_sim || !Thread.current[:alternative_executed].last
511:
      __weel_sim_stop(:otherwise,hw,pos,args.merge(:mode => Thread.current[:alternative_mode].last)) if __weel_sim
512:
    end # }}}
513:
 
514:
    # Defines a critical block (=Mutex)
515:
    def critical(id)# {{{
516:
      @__weel_critical ||= Mutex.new
517:
      semaphore = nil
518:
      @__weel_critical.synchronize do
519:
        @__weel_critical_sections ||= {}
520:
        semaphore = @__weel_critical_sections[id] ? @__weel_critical_sections[id] : Mutex.new
521:
        @__weel_critical_sections[id] = semaphore if id
522:
      end
523:
      semaphore.synchronize do
524:
        __weel_protect_yield(&Proc.new)
525:
      end
526:
    end # }}}
527:
 
528:
    # Defines a Cycle (loop/iteration)
529:
    def loop(condition,args={})# {{{
530:
      unless condition.is_a?(Array) && (condition[0].is_a?(Proc) || condition[0].is_a?(String)) && [:pre_test,:post_test].include?(condition[1]) && args.is_a?(Hash)
531:
        raise "condition must be called pre_test{} or post_test{}"
532:
      end
533:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
534:
      if __weel_is_in_search_mode
535:
        catch :escape do
536:
          __weel_protect_yield(&Proc.new)
537:
        end
538:
        if __weel_is_in_search_mode
539:
          return
540:
        else
541:
          ### in case it was a :post_test we wake inside the loop so we can check
542:
          ### condition first thing
543:
          condition[1] = :pre_test
544:
        end
545:
      end
546:
      if __weel_sim
547:
        cond = condition[0].is_a?(Proc) ? true : condition[0]
548:
        hw, pos = __weel_sim_start(:loop,args.merge(:testing=>condition[1],:condition=>cond))
549:
        catch :escape do
550:
          __weel_protect_yield(&Proc.new)
551:
        end
552:
        __weel_sim_stop(:loop,hw,pos,args.merge(:testing=>condition[1],:condition=>cond))
553:
        return
554:
      end
555:
      catch :escape do
556:
        case condition[1]
557:
          when :pre_test
558:
            while __weel_eval_condition(condition[0]) && self.__weel_state != :stopping && self.__weel_state != :stopped && self.__weel_state != :finishing
559:
              __weel_protect_yield(&Proc.new)
560:
            end
561:
          when :post_test
562:
            begin
563:
              __weel_protect_yield(&Proc.new)
564:
            end while __weel_eval_condition(condition[0]) && self.__weel_state != :stopping && self.__weel_state != :stopped && self.__weel_state != :finishing
565:
        end
566:
      end
567:
    end # }}}
568:
 
569:
    def test(code=nil,&blk)# {{{
570:
      code || blk
571:
    end # }}}
572:
    def pre_test(code=nil,&blk)# {{{
573:
      [code || blk, :pre_test]
574:
    end # }}}
575:
    def post_test(code=nil,&blk)# {{{
576:
      [code || blk, :post_test]
577:
    end # }}}
578:
 
579:
    def escape #{{{
580:
      return if __weel_is_in_search_mode
581:
      throw :escape
582:
    end #}}}
583:
    def terminate #{{{
584:
      return if __weel_is_in_search_mode
585:
      self.__weel_state = :finishing
586:
    end #}}}
587:
    def stop(position) #{{{
588:
      searchmode = __weel_is_in_search_mode(position)
589:
      return if searchmode
590:
      __weel_progress searchmode, position, true
591:
      self.__weel_state = :stopping
592:
    end #}}}
593:
 
594:
    def status # {{{
595:
      @__weel_status
596:
    end # }}}
597:
    def data # {{{
598:
      ReadHash.new(@__weel_data,__weel_sim)
599:
    end # }}}
600:
    def endpoints # {{{
601:
      ReadHash.new(@__weel_endpoints)
602:
    end # }}}
603:
 
604:
  private
605:
    def __weel_protect_yield(*local) #{{{
606:
      begin
607:
        yield(*local) if block_given?
608:
      rescue NameError => err # don't look into it, or it will explode
609:
        self.__weel_state = :stopping
610:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new("protect_yield: `#{err.name}` is not a thing that can be used. Maybe it is meant to be a string and you forgot quotes?"),nil)
611:
        nil
612:
      rescue => err
613:
        self.__weel_state = :stopping
614:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new(err.message),nil)
615:
        nil
616:
      end
617:
    end #}}}
618:
 
619:
    def __weel_eval_condition(condition) #{{{
620:
      begin
621:
        handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args unless condition.is_a?(Proc)
622:
        condition.is_a?(Proc) ? condition.call : handlerwrapper.test_condition(ReadStructure.new(@__weel_data,@__weel_endpoints),condition)
623:
      rescue NameError => err # don't look into it, or it will explode
624:
        # if you access $! here, BOOOM
625:
        self.__weel_state = :stopping
626:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new("eval_condition: `#{err.name}` is not a thing that can be used. Maybe it is meant to be a string and you forgot quotes?"),nil)
627:
        nil
628:
      rescue => err
629:
        self.__weel_state = :stopping
630:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new(err.message),nil)
631:
        nil
632:
      end
633:
    end #}}}
634:
 
635:
    def __weel_progress(searchmode, position, skip=false) #{{{
636:
      ipc = {}
637:
      if searchmode == :after
638:
        wp = WEEL::Position.new(position, :after, nil)
639:
        ipc[:after] = [wp]
640:
      else
641:
        if Thread.current[:branch_parent] && Thread.current[:branch_parent][:branch_position]
642:
          @__weel_positions.delete Thread.current[:branch_parent][:branch_position]
643:
          ipc[:unmark] ||= []
644:
          ipc[:unmark] << Thread.current[:branch_parent][:branch_position] rescue nil
645:
          Thread.current[:branch_parent][:branch_position] = nil
646:
        end
647:
        if Thread.current[:branch_position]
648:
          @__weel_positions.delete Thread.current[:branch_position]
649:
          ipc[:unmark] ||= []
650:
          ipc[:unmark] << Thread.current[:branch_position] rescue nil
651:
        end
652:
        wp = WEEL::Position.new(position, skip ? :after : :at, nil)
653:
        ipc[skip ? :after : :at] = [wp]
654:
      end
655:
      @__weel_positions << wp
656:
      Thread.current[:branch_position] = wp
657:
 
658:
      @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, ipc
659:
      wp
660:
    end #}}}
661:
 
662:
    def __weel_activity(position, type, endpoints, parameters, finalize, update=nil)# {{{
663:
      position = __weel_position_test position
664:
      begin
665:
        searchmode = __weel_is_in_search_mode(position)
666:
        return if searchmode == true
667:
        return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
668:
 
669:
        Thread.current[:continue] = Continue.new
670:
        handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args, endpoints.is_a?(Array) ? endpoints.map{ |ep| @__weel_endpoints[ep] }.compact : @__weel_endpoints[endpoints], position, Thread.current[:continue]
671:
 
672:
        if __weel_sim
673:
          handlerwrapper.simulate(:activity,:none,@__weel_sim += 1,Thread.current[:branch_sim_pos],:position => position,:parameters => parameters,:endpoints => endpoints,:type => type,:finalize => finalize.is_a?(String) ? finalize : nil)
674:
          return
675:
        end
676:
 
677:
        wp = __weel_progress searchmode, position
678:
 
679:
        # searchmode position is after, jump directly to vote_sync_after
680:
        raise Signal::Proceed if searchmode == :after
681:
 
682:
        case type
683:
          when :manipulate
684:
            raise Signal::Stop unless handlerwrapper.vote_sync_before
685:
            raise Signal::Skip if self.__weel_state == :stopping || self.__weel_state == :finishing
686:
 
687:
            if finalize.is_a?(Proc) || finalize.is_a?(String)
688:
              handlerwrapper.activity_manipulate_handle(parameters)
689:
              handlerwrapper.inform_activity_manipulate
690:
              if finalize.is_a?(Proc)
691:
                mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
692:
                mr.instance_eval(&finalize)
693:
              elsif finalize.is_a?(String)
694:
                mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
695:
                handlerwrapper.manipulate(mr,finalize)
696:
              end
697:
              handlerwrapper.inform_manipulate_change(
698:
                ((mr && mr.changed_status) ? @__weel_status : nil),
699:
                ((mr && mr.changed_data.any?) ? mr.changed_data.uniq : nil),
700:
                ((mr && mr.changed_endpoints.any?) ? mr.changed_endpoints.uniq : nil),
701:
                @__weel_data,
702:
                @__weel_endpoints
703:
              )
704:
              handlerwrapper.inform_activity_done
705:
              wp.detail = :after
706:
              @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :after => [wp]
707:
            end
708:
          when :call
709:
            params = { }
710:
            case parameters
711:
              when Hash
712:
                parameters.each do |k,p|
713:
                  if p.is_a?(Symbol) && @__weel_data.include?(p)
714:
                    params[k] = @__weel_data[p]
715:
                  else
716:
                    params[k] = p
717:
                  end
718:
                end
719:
              when Array
720:
                parameters.each_with_index do |p,i|
721:
                  if p.is_a?(Symbol) && @__weel_data.include?(p)
722:
                    params[p] = @__weel_data[p]
723:
                  else
724:
                    params[i] = p
725:
                  end
726:
                end
727:
              else
728:
                raise("invalid parameters")
729:
            end
730:
            raise Signal::Stop unless handlerwrapper.vote_sync_before(params)
731:
            raise Signal::Skip if self.__weel_state == :stopping || self.__weel_state == :finishing
732:
 
733:
            if @__weel_search_positions[position]
734:
              passthrough = @__weel_search_positions[position].passthrough
735:
              @__weel_search_positions[position].passthrough = nil
736:
            else
737:
              passthrough = nil
738:
            end
739:
 
740:
            handlerwrapper.activity_handle passthrough, params
741:
            wp.passthrough = handlerwrapper.activity_passthrough_value
742:
            unless wp.passthrough.nil?
743:
              @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :wait => [wp]
744:
            end
745:
            begin
746:
              # with loop if catching Signal::Again
747:
              # handshake call and wait until it finished
748:
              waitingresult = nil
749:
              waitingresult = Thread.current[:continue].wait unless Thread.current[:nolongernecessary] || self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped
750:
              raise waitingresult[1] if !waitingresult.nil? && waitingresult.is_a?(Array) && waitingresult.length == 2 && waitingresult[0] == WEEL::Signal::Error
751:
 
752:
              if Thread.current[:nolongernecessary]
753:
                handlerwrapper.activity_no_longer_necessary
754:
                raise Signal::NoLongerNecessary
755:
              end
756:
              if self.__weel_state == :stopping || self.__weel_state == :finishing
757:
                handlerwrapper.activity_stop
758:
                wp.passthrough = handlerwrapper.activity_passthrough_value
759:
                raise Signal::Proceed
760:
              end
761:
 
762:
              code = waitingresult == Signal::Again ? update : finalize
763:
              if code.is_a?(Proc) || code.is_a?(String)
764:
                handlerwrapper.inform_activity_manipulate
765:
                if code.is_a?(Proc)
766:
                  mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
767:
                  case code.arity
768:
                    when 1; mr.instance_exec(handlerwrapper.activity_result_value,&code)
769:
                    when 2; mr.instance_exec(handlerwrapper.activity_result_value,&code)
770:
                    else
771:
                      mr.instance_exec(&code)
772:
                  end
773:
                elsif code.is_a?(String)
774:
                  mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
775:
                  handlerwrapper.manipulate(mr,code,handlerwrapper.activity_result_value)
776:
                end
777:
                handlerwrapper.inform_manipulate_change(
778:
                  (mr.changed_status ? @__weel_status : nil),
779:
                  (mr.changed_data.any? ? mr.changed_data.uniq : nil),
780:
                  (mr.changed_endpoints.any? ? mr.changed_endpoints.uniq : nil),
781:
                  @__weel_data,
782:
                  @__weel_endpoints
783:
                )
784:
              end
785:
            end while waitingresult == Signal::Again
786:
            if handlerwrapper.activity_passthrough_value.nil?
787:
              handlerwrapper.inform_activity_done
788:
              wp.passthrough = nil
789:
              wp.detail = :after
790:
              @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :after => [wp]
791:
            end
792:
        end
793:
        raise Signal::Proceed
794:
      rescue Signal::SkipManipulate, Signal::Proceed
795:
        if self.__weel_state != :stopping && self.__weel_state != :finishing && !handlerwrapper.vote_sync_after
796:
          self.__weel_state = :stopping
797:
          wp.detail = :unmark
798:
        end
799:
      rescue Signal::NoLongerNecessary
800:
        @__weel_positions.delete wp
801:
        Thread.current[:branch_position] = nil
802:
        wp.detail = :unmark
803:
        @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :unmark => [wp]
804:
      rescue Signal::StopSkipManipulate, Signal::Stop
805:
        self.__weel_state = :stopping
806:
      rescue Signal::Skip
807:
        nil
808:
      rescue SyntaxError => se
809:
        handlerwrapper.inform_activity_failed se
810:
        self.__weel_state = :stopping
811:
      rescue => err
812:
        handlerwrapper.inform_activity_failed err
813:
        self.__weel_state = :stopping
814:
      ensure
815:
        Thread.current[:continue].clear if Thread.current[:continue] && Thread.current[:continue].is_a?(Continue)
816:
      end
817:
    end # }}}
818:
 
819:
    def __weel_recursive_print(thread,indent='')# {{{
820:
      p "#{indent}#{thread}"
821:
      if thread[:branches]
822:
        thread[:branches].each do |b|
823:
          __weel_recursive_print(b,indent+'  ')
824:
        end
825:
      end
826:
    end  # }}}
827:
    def __weel_recursive_continue(thread)# {{{
828:
      return unless thread
829:
      if thread.alive? && thread[:continue]
830:
        thread[:continue].continue
831:
      end
832:
      if thread.alive? && thread[:branch_event]
833:
        thread[:mutex].synchronize do
834:
          thread[:branch_event].continue unless thread[:branch_event].nil?
835:
        end
836:
      end
837:
      if thread[:branches]
838:
        thread[:branches].each do |b|
839:
          __weel_recursive_continue(b)
840:
        end
841:
      end
842:
    end  # }}}
843:
    def __weel_recursive_join(thread)# {{{
844:
      return unless thread
845:
      if thread.alive? && thread != Thread.current
846:
        thread.join
847:
      end
848:
      if thread[:branches]
849:
        thread[:branches].each do |b|
850:
          __weel_recursive_join(b)
851:
        end
852:
      end
853:
    end  # }}}
854:
 
855:
    def __weel_position_test(position)# {{{
856:
      if position.is_a?(Symbol) && position.to_s =~ /[a-zA-Z][a-zA-Z0-9_]*/
857:
        position
858:
      else
859:
        self.__weel_state = :stopping
860:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new("position (#{position}) not valid"),nil)
861:
      end
862:
    end # }}}
863:
 
864:
    def __weel_is_in_search_mode(position = nil)# {{{
865:
      branch = Thread.current
866:
      return false if @__weel_search_positions.empty? || branch[:branch_search] == false
867:
 
868:
      if position && @__weel_search_positions.include?(position) # matching searchpos => start execution from here
869:
        branch[:branch_search] = false # execute all activities in THIS branch (thread) after this point
870:
        while branch.key?(:branch_parent) # also all parent branches should execute activities after this point, additional branches spawned by parent branches should still be in search mode
871:
          branch = branch[:branch_parent]
872:
          branch[:branch_search] = false
873:
        end
874:
        @__weel_search_positions[position].detail == :after ? :after : false
875:
      else
876:
        branch[:branch_search] = true
877:
      end
878:
    end # }}}
879:
 
880:
    def __weel_sim #{{{
881:
      @__weel_state == :simulating
882:
    end #}}}
883:
 
884:
    def __weel_sim_start(what,options={}) #{{{
885:
      current_branch_sim_pos = Thread.current[:branch_sim_pos]
886:
      Thread.current[:branch_sim_pos] = @__weel_sim += 1
887:
      handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args
888:
      handlerwrapper.simulate(what,:start,Thread.current[:branch_sim_pos],current_branch_sim_pos,options)
889:
      [handlerwrapper, current_branch_sim_pos]
890:
    end #}}}
891:
 
892:
    def __weel_sim_stop(what,handlerwrapper,current_branch_sim_pos,options={}) #{{{
893:
      handlerwrapper.simulate(what,:end,Thread.current[:branch_sim_pos],current_branch_sim_pos,options)
894:
      Thread.current[:branch_sim_pos] = current_branch_sim_pos
895:
    end #}}}
896:
 
897:
  public
898:
    def __weel_finalize #{{{
899:
      __weel_recursive_join(@__weel_main)
900:
      @__weel_state = :stopped
901:
      @__weel_handlerwrapper::inform_state_change @__weel_handlerwrapper_args, @__weel_state
902:
    end #}}}
903:
 
904:
    def __weel_state=(newState)# {{{
905:
      return @__weel_state if newState == @__weel_state && @__weel_state != :ready
906:
 
907:
      @__weel_positions = Array.new if newState == :running
908:
      @__weel_state = newState
909:
 
910:
      if newState == :stopping || newState == :finishing
911:
        @__weel_status.nudge!
912:
        __weel_recursive_continue(@__weel_main)
913:
      end
914:
 
915:
      @__weel_handlerwrapper::inform_state_change @__weel_handlerwrapper_args, @__weel_state
916:
    end # }}}
917:
 
918:
  end # }}}
919:
 
920:
public
921:
  def positions # {{{
922:
    @dslr.__weel_positions
923:
  end # }}}
924:
 
925:
  # set the handlerwrapper
926:
  def handlerwrapper # {{{
927:
    @dslr.__weel_handlerwrapper
928:
  end # }}}
929:
  def handlerwrapper=(new_weel_handlerwrapper) # {{{
930:
    superclass = new_weel_handlerwrapper
931:
    while superclass
932:
      check_ok = true if superclass == WEEL::HandlerWrapperBase
933:
      superclass = superclass.superclass
934:
    end
935:
    raise "Handlerwrapper is not inherited from HandlerWrapperBase" unless check_ok
936:
    @dslr.__weel_handlerwrapper = new_weel_handlerwrapper
937:
  end # }}}
938:
 
939:
  # Get/Set the handlerwrapper arguments
940:
  def handlerwrapper_args # {{{
941:
    @dslr.__weel_handlerwrapper_args
942:
  end # }}}
943:
  def handlerwrapper_args=(args) # {{{
944:
    if args.class == Array
945:
      @dslr.__weel_handlerwrapper_args = args
946:
    end
947:
    nil
948:
  end #  }}}
949:
 
950:
  # Get the state of execution (ready|running|stopping|stopped|finished|simulating)
951:
  def state # {{{
952:
    @dslr.__weel_state
953:
  end #  }}}
954:
  def state_signal # {{{
955:
    handlerwrapper::inform_state_change handlerwrapper_args, state
956:
    state
957:
  end # }}}
958:
 
959:
  # Set search positions
960:
  # set new_weel_search to a boolean (or anything else) to start the process from beginning (reset serach positions)
961:
  def search(new_weel_search=false) # {{{
962:
    @dslr.__weel_search_positions.clear
963:
 
964:
    new_weel_search = [new_weel_search] if new_weel_search.is_a?(Position)
965:
 
966:
    if !new_weel_search.is_a?(Array) || new_weel_search.empty?
967:
      false
968:
    else
969:
      new_weel_search.each do |search_position|
970:
        @dslr.__weel_search_positions[search_position.position] = search_position
971:
      end
972:
      true
973:
    end
974:
  end # }}}
975:
 
976:
  def data(new_data=nil) # {{{
977:
    unless new_data.nil? || !new_data.is_a?(Hash)
978:
      new_data.each{ |k,v| @dslr.__weel_data[k] = v }
979:
    end
980:
    @dslr.__weel_data
981:
  end # }}}
982:
  def endpoints(new_endpoints=nil) # {{{
983:
    unless new_endpoints.nil? || !new_endpoints.is_a?(Hash)
984:
      new_endpoints.each{ |k,v| @dslr.__weel_endpoints[k] = v }
985:
    end
986:
    @dslr.__weel_endpoints
987:
  end # }}}
988:
  def endpoint(new_endpoints) # {{{
989:
    unless new_endpoints.nil? || !new_endpoints.is_a?(Hash) || !new_endpoints.length == 1
990:
      new_endpoints.each{ |k,v| @dslr.__weel_endpoints[k] = v }
991:
    end
992:
    nil
993:
  end # }}}
994:
  def status # {{{
995:
    @dslr.__weel_status
996:
  end # }}}
997:
 
998:
  # get/set workflow description
999:
  def description(&blk)
1000:
    self.description=(blk)
1001:
  end
1002:
  def description=(code) # {{{
1003:
    (class << self; self; end).class_eval do
1004:
      remove_method :__weel_control_flow if method_defined? :__weel_control_flow
1005:
      define_method :__weel_control_flow do |state,final_state=:finished|
1006:
        @dslr.__weel_positions.clear
1007:
        @dslr.__weel_state = state
1008:
        begin
1009:
          if code.is_a? Proc
1010:
            @dslr.instance_eval(&code)
1011:
          else
1012:
            @dslr.instance_eval(code)
1013:
          end
1014:
        rescue SyntaxError => se
1015:
          @dslr.__weel_state = :stopping
1016:
          @dslr.__weel_handlerwrapper::inform_syntax_error(@dslr.__weel_handlerwrapper_args,Exception.new(se.message),code)
1017:
        rescue NameError => err # don't look into it, or it will explode
1018:
          @dslr.__weel_state = :stopping
1019:
          @dslr.__weel_handlerwrapper::inform_syntax_error(@dslr.__weel_handlerwrapper_args,Exception.new("main: `#{err.name}` is not a thing that can be used. Maybe it is meant to be a string and you forgot quotes?"),code)
1020:
        rescue => err
1021:
          @dslr.__weel_state = :stopping
1022:
          @dslr.__weel_handlerwrapper::inform_syntax_error(@dslr.__weel_handlerwrapper_args,Exception.new(err.message),code)
1023:
        end
1024:
        if @dslr.__weel_state == :running || @dslr.__weel_state == :finishing
1025:
          ipc = { :unmark => [] }
1026:
          @dslr.__weel_positions.each{ |wp| ipc[:unmark] << wp }
1027:
          @dslr.__weel_positions.clear
1028:
          @dslr.__weel_handlerwrapper::inform_position_change(@dslr.__weel_handlerwrapper_args,ipc)
1029:
          @dslr.__weel_state = :finished
1030:
        end
1031:
        if @dslr.__weel_state == :simulating
1032:
          @dslr.__weel_state = final_state
1033:
        end
1034:
        if @dslr.__weel_state == :stopping
1035:
          @dslr.__weel_finalize
1036:
        end
1037:
      end
1038:
    end
1039:
  end # }}}
1040:
 
1041:
  # Stop the workflow execution
1042:
  def stop # {{{
1043:
    Thread.new do
1044:
      @dslr.__weel_state = :stopping
1045:
      @dslr.__weel_main.join if @dslr.__weel_main
1046:
    end
1047:
  end # }}}
1048:
  # Start the workflow execution
1049:
  def start # {{{
1050:
    return nil if @dslr.__weel_state != :ready && @dslr.__weel_state != :stopped
1051:
    @dslr.__weel_main = Thread.new do
1052:
      begin
1053:
        __weel_control_flow(:running)
1054:
      rescue => e
1055:
        puts e.message
1056:
        puts e.backtrace
1057:
        handlerwrapper::inform_handlerwrapper_error handlerwrapper_args, e
1058:
      end
1059:
    end
1060:
  end # }}}
1061:
 
1062:
  def sim # {{{
1063:
    stat = @dslr.__weel_state
1064:
    return nil unless stat == :ready || stat == :stopped
1065:
    @dslr.__weel_main = Thread.new do
1066:
      __weel_control_flow :simulating, stat
1067:
    end
1068:
  end # }}}
1069:
 
1070:
end