bud 0.1.0.pre1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +23 -0
 - data/{README → README.md} +6 -2
 - data/docs/cheat.md +1 -8
 - data/docs/intro.md +1 -1
 - data/lib/bud/aggs.rb +16 -16
 - data/lib/bud/bud_meta.rb +8 -15
 - data/lib/bud/collections.rb +85 -172
 - data/lib/bud/errors.rb +5 -1
 - data/lib/bud/executor/elements.rb +133 -118
 - data/lib/bud/executor/group.rb +6 -6
 - data/lib/bud/executor/join.rb +25 -22
 - data/lib/bud/metrics.rb +1 -1
 - data/lib/bud/monkeypatch.rb +18 -29
 - data/lib/bud/rebl.rb +5 -4
 - data/lib/bud/rewrite.rb +21 -160
 - data/lib/bud/source.rb +5 -5
 - data/lib/bud/state.rb +13 -12
 - data/lib/bud/storage/dbm.rb +13 -23
 - data/lib/bud/storage/zookeeper.rb +0 -4
 - data/lib/bud.rb +184 -162
 - metadata +144 -216
 - data/docs/deploy.md +0 -96
 - data/lib/bud/deploy/countatomicdelivery.rb +0 -38
 - data/lib/bud/joins.rb +0 -526
 
    
        data/lib/bud.rb
    CHANGED
    
    | 
         @@ -11,9 +11,7 @@ require 'bud/monkeypatch' 
     | 
|
| 
       11 
11 
     | 
    
         
             
            require 'bud/aggs'
         
     | 
| 
       12 
12 
     | 
    
         
             
            require 'bud/bud_meta'
         
     | 
| 
       13 
13 
     | 
    
         
             
            require 'bud/collections'
         
     | 
| 
       14 
     | 
    
         
            -
            require 'bud/joins'
         
     | 
| 
       15 
14 
     | 
    
         
             
            require 'bud/metrics'
         
     | 
| 
       16 
     | 
    
         
            -
            #require 'bud/meta_algebra'
         
     | 
| 
       17 
15 
     | 
    
         
             
            require 'bud/rtrace'
         
     | 
| 
       18 
16 
     | 
    
         
             
            require 'bud/server'
         
     | 
| 
       19 
17 
     | 
    
         
             
            require 'bud/state'
         
     | 
| 
         @@ -62,12 +60,11 @@ $bud_instances = {}        # Map from instance id => Bud instance 
     | 
|
| 
       62 
60 
     | 
    
         
             
            #
         
     | 
| 
       63 
61 
     | 
    
         
             
            # :main: Bud
         
     | 
| 
       64 
62 
     | 
    
         
             
            module Bud
         
     | 
| 
       65 
     | 
    
         
            -
              attr_reader : 
     | 
| 
       66 
     | 
    
         
            -
              attr_reader : 
     | 
| 
       67 
     | 
    
         
            -
              attr_reader :tables, :builtin_tables, :channels, :zk_tables, :dbm_tables, :sources, :sinks, :app_tables
         
     | 
| 
      
 63 
     | 
    
         
            +
              attr_reader :budtime, :inbound, :options, :meta_parser, :viz, :rtracer, :dsock
         
     | 
| 
      
 64 
     | 
    
         
            +
              attr_reader :tables, :builtin_tables, :channels, :zk_tables, :dbm_tables, :app_tables
         
     | 
| 
       68 
65 
     | 
    
         
             
              attr_reader :push_sources, :push_elems, :push_joins, :scanners, :merge_targets, :done_wiring
         
     | 
| 
       69 
     | 
    
         
            -
              attr_reader :this_stratum, :this_rule, :rule_orig_src, :done_bootstrap 
     | 
| 
       70 
     | 
    
         
            -
              attr_accessor : 
     | 
| 
      
 66 
     | 
    
         
            +
              attr_reader :this_stratum, :this_rule, :rule_orig_src, :done_bootstrap
         
     | 
| 
      
 67 
     | 
    
         
            +
              attr_accessor :stratified_rules
         
     | 
| 
       71 
68 
     | 
    
         
             
              attr_accessor :metrics, :periodics
         
     | 
| 
       72 
69 
     | 
    
         
             
              attr_accessor :this_rule_context, :qualified_name
         
     | 
| 
       73 
70 
     | 
    
         
             
              attr_reader :running_async
         
     | 
| 
         @@ -82,7 +79,7 @@ module Bud 
     | 
|
| 
       82 
79 
     | 
    
         
             
              # * operating system interaction
         
     | 
| 
       83 
80 
     | 
    
         
             
              #   * <tt>:stdin</tt>  if non-nil, reading from the +stdio+ collection results in reading from this +IO+ handle
         
     | 
| 
       84 
81 
     | 
    
         
             
              #   * <tt>:stdout</tt> writing to the +stdio+ collection results in writing to this +IO+ handle; defaults to <tt>$stdout</tt>
         
     | 
| 
       85 
     | 
    
         
            -
              #   * <tt>:signal_handling</tt> how to handle +SIGINT+ and +SIGTERM+.  If  
     | 
| 
      
 82 
     | 
    
         
            +
              #   * <tt>:signal_handling</tt> how to handle +SIGINT+ and +SIGTERM+.  If +:none+, these signals are ignored.  Else shutdown all bud instances.
         
     | 
| 
       86 
83 
     | 
    
         
             
              # * tracing and output
         
     | 
| 
       87 
84 
     | 
    
         
             
              #   * <tt>:quiet</tt> if true, suppress certain messages
         
     | 
| 
       88 
85 
     | 
    
         
             
              #   * <tt>:trace</tt> if true, generate +budvis+ outputs
         
     | 
| 
         @@ -106,19 +103,18 @@ module Bud 
     | 
|
| 
       106 
103 
     | 
    
         
             
              #   * <tt>:dbm_truncate</tt> if true, DBM-backed collections are opened with +OTRUNC+
         
     | 
| 
       107 
104 
     | 
    
         
             
              def initialize(options={})
         
     | 
| 
       108 
105 
     | 
    
         
             
                # capture the binding for a subsequent 'eval'. This ensures that local
         
     | 
| 
       109 
     | 
    
         
            -
                # variable names introduced later in this method don't interfere with 
     | 
| 
      
 106 
     | 
    
         
            +
                # variable names introduced later in this method don't interfere with
         
     | 
| 
       110 
107 
     | 
    
         
             
                # table names used in the eval block.
         
     | 
| 
       111 
108 
     | 
    
         
             
                options[:dump_rewrite] ||= ENV["BUD_DUMP_REWRITE"].to_i > 0
         
     | 
| 
       112 
109 
     | 
    
         
             
                options[:dump_ast]     ||= ENV["BUD_DUMP_AST"].to_i > 0
         
     | 
| 
       113 
110 
     | 
    
         
             
                options[:print_wiring] ||= ENV["BUD_PRINT_WIRING"].to_i > 0
         
     | 
| 
       114 
111 
     | 
    
         
             
                @qualified_name = ""
         
     | 
| 
       115 
112 
     | 
    
         
             
                @tables = {}
         
     | 
| 
       116 
     | 
    
         
            -
                @table_meta = []
         
     | 
| 
       117 
     | 
    
         
            -
                @stratified_rules = []
         
     | 
| 
       118 
113 
     | 
    
         
             
                @channels = {}
         
     | 
| 
       119 
     | 
    
         
            -
                @push_elems = {}
         
     | 
| 
       120 
114 
     | 
    
         
             
                @dbm_tables = {}
         
     | 
| 
       121 
115 
     | 
    
         
             
                @zk_tables = {}
         
     | 
| 
      
 116 
     | 
    
         
            +
                @stratified_rules = []
         
     | 
| 
      
 117 
     | 
    
         
            +
                @push_elems = {}
         
     | 
| 
       122 
118 
     | 
    
         
             
                @callbacks = {}
         
     | 
| 
       123 
119 
     | 
    
         
             
                @callback_id = 0
         
     | 
| 
       124 
120 
     | 
    
         
             
                @shutdown_callbacks = {}
         
     | 
| 
         @@ -133,15 +129,10 @@ module Bud 
     | 
|
| 
       133 
129 
     | 
    
         
             
                @done_bootstrap = false
         
     | 
| 
       134 
130 
     | 
    
         
             
                @done_wiring = false
         
     | 
| 
       135 
131 
     | 
    
         
             
                @instance_id = ILLEGAL_INSTANCE_ID # Assigned when we start running
         
     | 
| 
       136 
     | 
    
         
            -
                @sources = {}
         
     | 
| 
       137 
     | 
    
         
            -
                @sinks = {}
         
     | 
| 
       138 
132 
     | 
    
         
             
                @metrics = {}
         
     | 
| 
       139 
133 
     | 
    
         
             
                @endtime = nil
         
     | 
| 
       140 
134 
     | 
    
         
             
                @this_stratum = 0
         
     | 
| 
       141 
135 
     | 
    
         
             
                @push_sorted_elems = nil
         
     | 
| 
       142 
     | 
    
         
            -
             
     | 
| 
       143 
     | 
    
         
            -
                # XXX This variable is unused in the Push executor
         
     | 
| 
       144 
     | 
    
         
            -
                @stratum_first_iter = false
         
     | 
| 
       145 
136 
     | 
    
         
             
                @running_async = false
         
     | 
| 
       146 
137 
     | 
    
         
             
                @bud_started = false
         
     | 
| 
       147 
138 
     | 
    
         | 
| 
         @@ -155,10 +146,7 @@ module Bud 
     | 
|
| 
       155 
146 
     | 
    
         
             
                # number won't be known until we start EM
         
     | 
| 
       156 
147 
     | 
    
         | 
| 
       157 
148 
     | 
    
         
             
                builtin_state
         
     | 
| 
       158 
     | 
    
         
            -
             
     | 
| 
       159 
149 
     | 
    
         
             
                resolve_imports
         
     | 
| 
       160 
     | 
    
         
            -
             
     | 
| 
       161 
     | 
    
         
            -
                # Invoke all the user-defined state blocks and initialize builtin state.
         
     | 
| 
       162 
150 
     | 
    
         
             
                call_state_methods
         
     | 
| 
       163 
151 
     | 
    
         | 
| 
       164 
152 
     | 
    
         
             
                @declarations = self.class.instance_methods.select {|m| m =~ /^__bloom__.+$/}.map {|m| m.to_s}
         
     | 
| 
         @@ -173,7 +161,7 @@ module Bud 
     | 
|
| 
       173 
161 
     | 
    
         
             
                  @scanners = num_strata.times.map{{}}
         
     | 
| 
       174 
162 
     | 
    
         
             
                  @push_sources = num_strata.times.map{{}}
         
     | 
| 
       175 
163 
     | 
    
         
             
                  @push_joins = num_strata.times.map{[]}
         
     | 
| 
       176 
     | 
    
         
            -
                  @merge_targets = num_strata.times.map{ 
     | 
| 
      
 164 
     | 
    
         
            +
                  @merge_targets = num_strata.times.map{Set.new}
         
     | 
| 
       177 
165 
     | 
    
         
             
                end
         
     | 
| 
       178 
166 
     | 
    
         
             
              end
         
     | 
| 
       179 
167 
     | 
    
         | 
| 
         @@ -182,7 +170,7 @@ module Bud 
     | 
|
| 
       182 
170 
     | 
    
         
             
                begin
         
     | 
| 
       183 
171 
     | 
    
         
             
                  klass = Module.const_get(class_name.to_sym)
         
     | 
| 
       184 
172 
     | 
    
         
             
                  unless klass.is_a? Class
         
     | 
| 
       185 
     | 
    
         
            -
                    raise Bud::Error, " 
     | 
| 
      
 173 
     | 
    
         
            +
                    raise Bud::Error, "internal error: #{class_name} is in use"
         
     | 
| 
       186 
174 
     | 
    
         
             
                  end
         
     | 
| 
       187 
175 
     | 
    
         
             
                rescue NameError # exception if class class_name doesn't exist
         
     | 
| 
       188 
176 
     | 
    
         
             
                end
         
     | 
| 
         @@ -207,6 +195,11 @@ module Bud 
     | 
|
| 
       207 
195 
     | 
    
         
             
                instance_variable_get(name) if instance_variable_defined? name
         
     | 
| 
       208 
196 
     | 
    
         
             
              end
         
     | 
| 
       209 
197 
     | 
    
         | 
| 
      
 198 
     | 
    
         
            +
              def budtime
         
     | 
| 
      
 199 
     | 
    
         
            +
                toplevel? ? @budtime : toplevel.budtime
         
     | 
| 
      
 200 
     | 
    
         
            +
              end
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
              private
         
     | 
| 
       210 
203 
     | 
    
         
             
              def import_defs
         
     | 
| 
       211 
204 
     | 
    
         
             
                @imported_defs = {} # mod name -> Module map
         
     | 
| 
       212 
205 
     | 
    
         
             
                self.class.ancestors.each do |anc|
         
     | 
| 
         @@ -215,7 +208,7 @@ module Bud 
     | 
|
| 
       215 
208 
     | 
    
         
             
                    anc_imp_tbl.each do |nm, mod|
         
     | 
| 
       216 
209 
     | 
    
         
             
                      # Ensure that two modules have not been imported under one alias.
         
     | 
| 
       217 
210 
     | 
    
         
             
                      if @imported_defs.member? nm and not @imported_defs[nm] == anc_imp_tbl[nm]
         
     | 
| 
       218 
     | 
    
         
            -
                          raise Bud::CompileError, " 
     | 
| 
      
 211 
     | 
    
         
            +
                          raise Bud::CompileError, "conflicting imports: modules #{@imported_defs[nm]} and #{anc_imp_tbl[nm]} both are imported as '#{nm}'"
         
     | 
| 
       219 
212 
     | 
    
         
             
                      end
         
     | 
| 
       220 
213 
     | 
    
         
             
                      @imported_defs[nm] = mod
         
     | 
| 
       221 
214 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -224,10 +217,6 @@ module Bud 
     | 
|
| 
       224 
217 
     | 
    
         
             
                @imported_defs ||= self.class.ancestors.inject({}) {|tbl, e| tbl.merge(e.bud_import_table)}
         
     | 
| 
       225 
218 
     | 
    
         
             
              end
         
     | 
| 
       226 
219 
     | 
    
         | 
| 
       227 
     | 
    
         
            -
              def budtime
         
     | 
| 
       228 
     | 
    
         
            -
                toplevel? ?  @budtime : toplevel.budtime
         
     | 
| 
       229 
     | 
    
         
            -
              end
         
     | 
| 
       230 
     | 
    
         
            -
             
     | 
| 
       231 
220 
     | 
    
         
             
              # absorb rules and dependencies from imported modules. The corresponding module instantiations
         
     | 
| 
       232 
221 
     | 
    
         
             
              # would themselves have resolved their own imports.
         
     | 
| 
       233 
222 
     | 
    
         
             
              def resolve_imports
         
     | 
| 
         @@ -236,47 +225,51 @@ module Bud 
     | 
|
| 
       236 
225 
     | 
    
         
             
                import_tbl.each_pair do |local_name, mod_name|
         
     | 
| 
       237 
226 
     | 
    
         
             
                  # corresponding to "import <mod_name> => :<local_name>"
         
     | 
| 
       238 
227 
     | 
    
         
             
                  mod_inst = send(local_name)
         
     | 
| 
       239 
     | 
    
         
            -
                  qlocal_name = toplevel? ? local_name.to_s : self.qualified_name + "." + local_name.to_s
         
     | 
| 
       240 
228 
     | 
    
         
             
                  if mod_inst.nil?
         
     | 
| 
       241 
229 
     | 
    
         
             
                    # create wrapper instances
         
     | 
| 
       242 
230 
     | 
    
         
             
                    #puts "=== resolving #{self}.#{mod_name} => #{local_name}"
         
     | 
| 
       243 
231 
     | 
    
         
             
                    klass = module_wrapper_class(mod_name)
         
     | 
| 
       244 
     | 
    
         
            -
                     
     | 
| 
      
 232 
     | 
    
         
            +
                    qlocal_name = toplevel? ? local_name.to_s : "#{self.qualified_name}.#{local_name}"
         
     | 
| 
      
 233 
     | 
    
         
            +
                    # this instantiation will resolve the imported module's own imports
         
     | 
| 
      
 234 
     | 
    
         
            +
                    mod_inst = klass.new(:toplevel => toplevel, :qualified_name => qlocal_name)
         
     | 
| 
       245 
235 
     | 
    
         
             
                    instance_variable_set("@#{local_name}", mod_inst)
         
     | 
| 
       246 
236 
     | 
    
         
             
                  end
         
     | 
| 
      
 237 
     | 
    
         
            +
                  # Absorb the module wrapper's user-defined state.
         
     | 
| 
       247 
238 
     | 
    
         
             
                  mod_inst.tables.each_pair do |name, t|
         
     | 
| 
       248 
     | 
    
         
            -
                    #  
     | 
| 
      
 239 
     | 
    
         
            +
                    # Don't try to import module definitions for builtin Bud state. Note
         
     | 
| 
      
 240 
     | 
    
         
            +
                    # that @tables only includes the builtin tables, because resolve_imports
         
     | 
| 
      
 241 
     | 
    
         
            +
                    # is called before user-defined state blocks are run.
         
     | 
| 
       249 
242 
     | 
    
         
             
                    unless @tables.has_key? t.tabname
         
     | 
| 
       250 
     | 
    
         
            -
                      qname =  
     | 
| 
       251 
     | 
    
         
            -
                      tables[qname] = t
         
     | 
| 
      
 243 
     | 
    
         
            +
                      qname = "#{local_name}.#{name}"
         
     | 
| 
      
 244 
     | 
    
         
            +
                      tables[qname.to_sym] = t
         
     | 
| 
       252 
245 
     | 
    
         
             
                    end
         
     | 
| 
       253 
246 
     | 
    
         
             
                  end
         
     | 
| 
       254 
247 
     | 
    
         
             
                  mod_inst.t_rules.each do |imp_rule|
         
     | 
| 
       255 
     | 
    
         
            -
                    qname = local_name 
     | 
| 
      
 248 
     | 
    
         
            +
                    qname = "#{local_name}.#{imp_rule.lhs}"
         
     | 
| 
       256 
249 
     | 
    
         
             
                    self.t_rules << [imp_rule.bud_obj, imp_rule.rule_id, qname, imp_rule.op,
         
     | 
| 
       257 
     | 
    
         
            -
             
     | 
| 
      
 250 
     | 
    
         
            +
                                     imp_rule.src, imp_rule.orig_src, imp_rule.nm_funcs_called]
         
     | 
| 
       258 
251 
     | 
    
         
             
                  end
         
     | 
| 
       259 
252 
     | 
    
         
             
                  mod_inst.t_depends.each do |imp_dep|
         
     | 
| 
       260 
     | 
    
         
            -
                    qlname = local_name 
     | 
| 
       261 
     | 
    
         
            -
                    qrname = local_name 
     | 
| 
       262 
     | 
    
         
            -
                    self.t_depends << [imp_dep.bud_obj, imp_dep.rule_id, qlname, 
     | 
| 
      
 253 
     | 
    
         
            +
                    qlname = "#{local_name}.#{imp_dep.lhs}"
         
     | 
| 
      
 254 
     | 
    
         
            +
                    qrname = "#{local_name}.#{imp_dep.body}"
         
     | 
| 
      
 255 
     | 
    
         
            +
                    self.t_depends << [imp_dep.bud_obj, imp_dep.rule_id, qlname,
         
     | 
| 
      
 256 
     | 
    
         
            +
                                       imp_dep.op, qrname, imp_dep.nm]
         
     | 
| 
       263 
257 
     | 
    
         
             
                  end
         
     | 
| 
       264 
258 
     | 
    
         
             
                  mod_inst.t_provides.each do |imp_pro|
         
     | 
| 
       265 
     | 
    
         
            -
                    qintname = local_name 
     | 
| 
      
 259 
     | 
    
         
            +
                    qintname = "#{local_name}.#{imp_pro.interface}"
         
     | 
| 
       266 
260 
     | 
    
         
             
                    self.t_provides << [qintname, imp_pro.input]
         
     | 
| 
       267 
261 
     | 
    
         
             
                  end
         
     | 
| 
       268 
262 
     | 
    
         
             
                  mod_inst.channels.each do |name, ch|
         
     | 
| 
       269 
     | 
    
         
            -
                    qname =  
     | 
| 
      
 263 
     | 
    
         
            +
                    qname = "#{local_name}.#{name}"
         
     | 
| 
       270 
264 
     | 
    
         
             
                    @channels[qname.to_sym] = ch
         
     | 
| 
       271 
265 
     | 
    
         
             
                  end
         
     | 
| 
       272 
266 
     | 
    
         
             
                  mod_inst.dbm_tables.each do |name, t|
         
     | 
| 
       273 
     | 
    
         
            -
                    qname =  
     | 
| 
      
 267 
     | 
    
         
            +
                    qname = "#{local_name}.#{name}"
         
     | 
| 
       274 
268 
     | 
    
         
             
                    @dbm_tables[qname.to_sym] = t
         
     | 
| 
       275 
269 
     | 
    
         
             
                  end
         
     | 
| 
       276 
270 
     | 
    
         
             
                  mod_inst.periodics.each do |p|
         
     | 
| 
       277 
     | 
    
         
            -
                    qname =  
     | 
| 
       278 
     | 
    
         
            -
                     
     | 
| 
       279 
     | 
    
         
            -
                    @periodics << [p.pername, p.period]
         
     | 
| 
      
 271 
     | 
    
         
            +
                    qname = "#{local_name}.#{p.pername}"
         
     | 
| 
      
 272 
     | 
    
         
            +
                    @periodics << [qname.to_sym, p.period]
         
     | 
| 
       280 
273 
     | 
    
         
             
                  end
         
     | 
| 
       281 
274 
     | 
    
         
             
                end
         
     | 
| 
       282 
275 
     | 
    
         | 
| 
         @@ -306,47 +299,30 @@ module Bud 
     | 
|
| 
       306 
299 
     | 
    
         
             
                end
         
     | 
| 
       307 
300 
     | 
    
         
             
              end
         
     | 
| 
       308 
301 
     | 
    
         | 
| 
       309 
     | 
    
         
            -
              # Evaluate all bootstrap blocks and tick deltas
         
     | 
| 
       310 
     | 
    
         
            -
              def do_bootstrap
         
     | 
| 
       311 
     | 
    
         
            -
                # evaluate bootstrap for imported modules
         
     | 
| 
       312 
     | 
    
         
            -
                @this_rule_context = self
         
     | 
| 
       313 
     | 
    
         
            -
                imported = import_defs.keys
         
     | 
| 
       314 
     | 
    
         
            -
                imported.each do |mod_alias|
         
     | 
| 
       315 
     | 
    
         
            -
                  wrapper = import_instance mod_alias
         
     | 
| 
       316 
     | 
    
         
            -
                  wrapper.do_bootstrap
         
     | 
| 
       317 
     | 
    
         
            -
                end
         
     | 
| 
       318 
     | 
    
         
            -
                self.class.ancestors.reverse.each do |anc|
         
     | 
| 
       319 
     | 
    
         
            -
                  anc.instance_methods(false).each do |m|
         
     | 
| 
       320 
     | 
    
         
            -
                    if /^__bootstrap__/.match m
         
     | 
| 
       321 
     | 
    
         
            -
                      self.method(m.to_sym).call
         
     | 
| 
       322 
     | 
    
         
            -
                    end
         
     | 
| 
       323 
     | 
    
         
            -
                  end
         
     | 
| 
       324 
     | 
    
         
            -
                end
         
     | 
| 
       325 
     | 
    
         
            -
                bootstrap
         
     | 
| 
       326 
     | 
    
         
            -
             
     | 
| 
       327 
     | 
    
         
            -
                @tables.each_value {|t| t.bootstrap} if toplevel == self
         
     | 
| 
       328 
     | 
    
         
            -
                @done_bootstrap = true
         
     | 
| 
       329 
     | 
    
         
            -
              end
         
     | 
| 
       330 
     | 
    
         
            -
             
     | 
| 
       331 
302 
     | 
    
         
             
              def do_wiring
         
     | 
| 
      
 303 
     | 
    
         
            +
                @num_strata = @stratified_rules.length
         
     | 
| 
      
 304 
     | 
    
         
            +
             
     | 
| 
       332 
305 
     | 
    
         
             
                @stratified_rules.each_with_index { |rules, stratum| eval_rules(rules, stratum) }
         
     | 
| 
       333 
306 
     | 
    
         | 
| 
       334 
     | 
    
         
            -
                #  
     | 
| 
       335 
     | 
    
         
            -
                # We start @app_tables off as a set, then convert to 
     | 
| 
       336 
     | 
    
         
            -
                 
     | 
| 
      
 307 
     | 
    
         
            +
                # Prepare list of tables that will be actively used at run time. First, all
         
     | 
| 
      
 308 
     | 
    
         
            +
                # the user-defined ones.  We start @app_tables off as a set, then convert to
         
     | 
| 
      
 309 
     | 
    
         
            +
                # an array later.
         
     | 
| 
      
 310 
     | 
    
         
            +
                @app_tables = (@tables.keys - @builtin_tables.keys).map {|t| @tables[t]}.to_set
         
     | 
| 
      
 311 
     | 
    
         
            +
             
     | 
| 
       337 
312 
     | 
    
         
             
                # Check scan and merge_targets to see if any builtin_tables need to be added as well.
         
     | 
| 
       338 
313 
     | 
    
         
             
                @scanners.each do |scs|
         
     | 
| 
       339 
     | 
    
         
            -
                  scs. 
     | 
| 
      
 314 
     | 
    
         
            +
                  @app_tables += scs.values.map {|s| s.collection}
         
     | 
| 
      
 315 
     | 
    
         
            +
                end
         
     | 
| 
      
 316 
     | 
    
         
            +
                @merge_targets.each do |mts| #mts == merge_targets at stratum
         
     | 
| 
      
 317 
     | 
    
         
            +
                  @app_tables += mts
         
     | 
| 
       340 
318 
     | 
    
         
             
                end
         
     | 
| 
       341 
     | 
    
         
            -
                @merge_targets.each{|mts| #mts == merge_targets at stratum
         
     | 
| 
       342 
     | 
    
         
            -
                  mts.each_key {|t| @app_tables << t}
         
     | 
| 
       343 
     | 
    
         
            -
                }
         
     | 
| 
       344 
319 
     | 
    
         
             
                @app_tables = @app_tables.nil? ? [] : @app_tables.to_a
         
     | 
| 
       345 
320 
     | 
    
         | 
| 
       346 
321 
     | 
    
         
             
                # for each stratum create a sorted list of push elements in topological order
         
     | 
| 
       347 
322 
     | 
    
         
             
                @push_sorted_elems = []
         
     | 
| 
       348 
323 
     | 
    
         
             
                @scanners.each do |scs|  # scs's values constitute scanners at a stratum
         
     | 
| 
       349 
     | 
    
         
            -
                  # start with scanners and transitively add all reachable elements in a 
     | 
| 
      
 324 
     | 
    
         
            +
                  # start with scanners and transitively add all reachable elements in a
         
     | 
| 
      
 325 
     | 
    
         
            +
                  # breadth-first order
         
     | 
| 
       350 
326 
     | 
    
         
             
                  working = scs.values
         
     | 
| 
       351 
327 
     | 
    
         
             
                  seen = Set.new(working)
         
     | 
| 
       352 
328 
     | 
    
         
             
                  sorted_elems = [] # sorted elements in this stratum
         
     | 
| 
         @@ -366,9 +342,9 @@ module Bud 
     | 
|
| 
       366 
342 
     | 
    
         
             
                  @push_sorted_elems << sorted_elems
         
     | 
| 
       367 
343 
     | 
    
         
             
                end
         
     | 
| 
       368 
344 
     | 
    
         | 
| 
       369 
     | 
    
         
            -
                @merge_targets.each_with_index do | 
     | 
| 
      
 345 
     | 
    
         
            +
                @merge_targets.each_with_index do |stratum_targets, stratum|
         
     | 
| 
       370 
346 
     | 
    
         
             
                  @scanners[stratum].each_value do |s|
         
     | 
| 
       371 
     | 
    
         
            -
                     
     | 
| 
      
 347 
     | 
    
         
            +
                    stratum_targets << s.collection
         
     | 
| 
       372 
348 
     | 
    
         
             
                  end
         
     | 
| 
       373 
349 
     | 
    
         
             
                end
         
     | 
| 
       374 
350 
     | 
    
         | 
| 
         @@ -379,12 +355,27 @@ module Bud 
     | 
|
| 
       379 
355 
     | 
    
         
             
                  end
         
     | 
| 
       380 
356 
     | 
    
         
             
                end
         
     | 
| 
       381 
357 
     | 
    
         | 
| 
      
 358 
     | 
    
         
            +
                # create sets of elements and collections to invalidate or rescan at the beginning of each tick.
         
     | 
| 
       382 
359 
     | 
    
         
             
                prepare_invalidation_scheme
         
     | 
| 
       383 
360 
     | 
    
         | 
| 
      
 361 
     | 
    
         
            +
                # For all tables that are accessed (scanned) in a stratum higher than the one they are updated in, set
         
     | 
| 
      
 362 
     | 
    
         
            +
                # a flag to track deltas accumulated in that tick (see: collection.tick_delta)
         
     | 
| 
      
 363 
     | 
    
         
            +
                stratum_accessed = {}
         
     | 
| 
      
 364 
     | 
    
         
            +
                (@num_strata-1).downto(0) do |stratum|
         
     | 
| 
      
 365 
     | 
    
         
            +
                  @scanners[stratum].each_value do |s|
         
     | 
| 
      
 366 
     | 
    
         
            +
                    stratum_accessed[s.collection] ||= stratum
         
     | 
| 
      
 367 
     | 
    
         
            +
                  end
         
     | 
| 
      
 368 
     | 
    
         
            +
                end
         
     | 
| 
      
 369 
     | 
    
         
            +
                @merge_targets.each_with_index do |stratum_targets, stratum|
         
     | 
| 
      
 370 
     | 
    
         
            +
                  stratum_targets.each {|tab|
         
     | 
| 
      
 371 
     | 
    
         
            +
                    tab.accumulate_tick_deltas = true if stratum_accessed[tab] and stratum_accessed[tab] > stratum
         
     | 
| 
      
 372 
     | 
    
         
            +
                  }
         
     | 
| 
      
 373 
     | 
    
         
            +
                end
         
     | 
| 
      
 374 
     | 
    
         
            +
             
     | 
| 
       384 
375 
     | 
    
         
             
                @done_wiring = true
         
     | 
| 
       385 
376 
     | 
    
         
             
                if @options[:print_wiring]
         
     | 
| 
       386 
     | 
    
         
            -
                  @push_sources.each do |strat| 
     | 
| 
       387 
     | 
    
         
            -
                    strat.each_value{|src| src.print_wiring}
         
     | 
| 
      
 377 
     | 
    
         
            +
                  @push_sources.each do |strat|
         
     | 
| 
      
 378 
     | 
    
         
            +
                    strat.each_value {|src| src.print_wiring}
         
     | 
| 
       388 
379 
     | 
    
         
             
                  end
         
     | 
| 
       389 
380 
     | 
    
         
             
                end
         
     | 
| 
       390 
381 
     | 
    
         
             
              end
         
     | 
| 
         @@ -394,9 +385,9 @@ module Bud 
     | 
|
| 
       394 
385 
     | 
    
         
             
              # permits us to preserve data from one tick to the next, and to keep things in incremental mode unless there's a
         
     | 
| 
       395 
386 
     | 
    
         
             
              # negation.
         
     | 
| 
       396 
387 
     | 
    
         
             
              # This scheme solves the following constraints.
         
     | 
| 
       397 
     | 
    
         
            -
              # 1. A full scan of an  
     | 
| 
      
 388 
     | 
    
         
            +
              # 1. A full scan of an element's contents results in downstream elements getting full scans themselves (i.e no \
         
     | 
| 
       398 
389 
     | 
    
         
             
              #    deltas). This effect is transitive.
         
     | 
| 
       399 
     | 
    
         
            -
              # 2. Invalidation of an element's cache results in rebuilding of the cache and a consequent fullscan
         
     | 
| 
      
 390 
     | 
    
         
            +
              # 2. Invalidation of an element's cache results in rebuilding of the cache and a consequent fullscan. See next.
         
     | 
| 
       400 
391 
     | 
    
         
             
              # 3. Invalidation of an element requires upstream elements to rescan their contents, or to transitively pass the
         
     | 
| 
       401 
392 
     | 
    
         
             
              #    request on further upstream. Any element that has a cache can rescan without passing on the request to higher
         
     | 
| 
       402 
393 
     | 
    
         
             
              #    levels.
         
     | 
| 
         @@ -407,16 +398,13 @@ module Bud 
     | 
|
| 
       407 
398 
     | 
    
         
             
              # scanner[stratum].invalidate = Set of elements to additionally invalidate if the scanner's table is invalidated at
         
     | 
| 
       408 
399 
     | 
    
         
             
              #  run-time
         
     | 
| 
       409 
400 
     | 
    
         
             
              # scanner[stratum].rescan = Similar to above.
         
     | 
| 
       410 
     | 
    
         
            -
             
     | 
| 
       411 
     | 
    
         
            -
             
     | 
| 
       412 
401 
     | 
    
         
             
              def prepare_invalidation_scheme
         
     | 
| 
       413 
     | 
    
         
            -
                num_strata = 
     | 
| 
      
 402 
     | 
    
         
            +
                num_strata = @push_sorted_elems.size
         
     | 
| 
       414 
403 
     | 
    
         
             
                if $BUD_SAFE
         
     | 
| 
       415 
404 
     | 
    
         
             
                  @app_tables = @tables.values # No tables excluded
         
     | 
| 
       416 
405 
     | 
    
         | 
| 
       417 
     | 
    
         
            -
                  invalidate = Set.new
         
     | 
| 
       418 
406 
     | 
    
         
             
                  rescan = Set.new
         
     | 
| 
       419 
     | 
    
         
            -
                  @app_tables. 
     | 
| 
      
 407 
     | 
    
         
            +
                  invalidate = @app_tables.select {|t| t.class <= BudScratch}.to_set
         
     | 
| 
       420 
408 
     | 
    
         
             
                  num_strata.times do |stratum|
         
     | 
| 
       421 
409 
     | 
    
         
             
                    @push_sorted_elems[stratum].each do |elem|
         
     | 
| 
       422 
410 
     | 
    
         
             
                      invalidate << elem
         
     | 
| 
         @@ -430,35 +418,53 @@ module Bud 
     | 
|
| 
       430 
418 
     | 
    
         
             
                  return
         
     | 
| 
       431 
419 
     | 
    
         
             
                end
         
     | 
| 
       432 
420 
     | 
    
         | 
| 
      
 421 
     | 
    
         
            +
                # By default, all tables are considered sources unless they appear on the
         
     | 
| 
      
 422 
     | 
    
         
            +
                # lhs.  We only consider non-temporal rules because invalidation is only
         
     | 
| 
      
 423 
     | 
    
         
            +
                # about this tick.  Also, we track (in nm_targets) those tables that are the
         
     | 
| 
      
 424 
     | 
    
         
            +
                # targets of user-defined code blocks that call non-monotonic functions
         
     | 
| 
      
 425 
     | 
    
         
            +
                # (such as budtime). Elements that feed these tables are forced to rescan
         
     | 
| 
      
 426 
     | 
    
         
            +
                # their contents, and thus forced to re-execute these code blocks.
         
     | 
| 
      
 427 
     | 
    
         
            +
                nm_targets = Set.new
         
     | 
| 
      
 428 
     | 
    
         
            +
                t_rules.each do |rule|
         
     | 
| 
      
 429 
     | 
    
         
            +
                  lhs = rule.lhs.to_sym
         
     | 
| 
      
 430 
     | 
    
         
            +
                  @tables[lhs].is_source = false if rule.op == "<="
         
     | 
| 
      
 431 
     | 
    
         
            +
                  nm_targets << lhs if rule.nm_funcs_called
         
     | 
| 
      
 432 
     | 
    
         
            +
                end
         
     | 
| 
       433 
433 
     | 
    
         | 
| 
       434 
     | 
    
         
            -
                #  
     | 
| 
       435 
     | 
    
         
            -
                #  
     | 
| 
       436 
     | 
    
         
            -
                 
     | 
| 
       437 
     | 
    
         
            -
             
     | 
| 
       438 
     | 
    
         
            -
                invalidate = Set.new
         
     | 
| 
      
 434 
     | 
    
         
            +
                # Compute a set of tables and elements that should be explicitly told to
         
     | 
| 
      
 435 
     | 
    
         
            +
                # invalidate or rescan.  Start with a set of tables that always invalidate
         
     | 
| 
      
 436 
     | 
    
         
            +
                # and elements that always rescan.
         
     | 
| 
      
 437 
     | 
    
         
            +
                invalidate = @app_tables.select {|t| t.invalidate_at_tick}.to_set
         
     | 
| 
       439 
438 
     | 
    
         
             
                rescan = Set.new
         
     | 
| 
       440 
     | 
    
         
            -
             
     | 
| 
       441 
     | 
    
         
            -
                # Start with a set of tables that always invalidate, and elements that always rescan
         
     | 
| 
       442 
     | 
    
         
            -
                @app_tables.each {|t| invalidate << t if t.invalidate_at_tick}
         
     | 
| 
      
 439 
     | 
    
         
            +
             
     | 
| 
       443 
440 
     | 
    
         
             
                num_strata.times do |stratum|
         
     | 
| 
       444 
441 
     | 
    
         
             
                  @push_sorted_elems[stratum].each do |elem|
         
     | 
| 
       445 
     | 
    
         
            -
                     
     | 
| 
      
 442 
     | 
    
         
            +
                    if elem.rescan_at_tick
         
     | 
| 
      
 443 
     | 
    
         
            +
                      rescan << elem
         
     | 
| 
      
 444 
     | 
    
         
            +
                    end
         
     | 
| 
      
 445 
     | 
    
         
            +
             
     | 
| 
      
 446 
     | 
    
         
            +
                    if elem.outputs.any?{|tab| not(tab.class <= PushElement) and nm_targets.member? tab.qualified_tabname.to_sym }
         
     | 
| 
      
 447 
     | 
    
         
            +
                      rescan += elem.wired_by
         
     | 
| 
      
 448 
     | 
    
         
            +
                    end
         
     | 
| 
       446 
449 
     | 
    
         
             
                  end
         
     | 
| 
       447 
450 
     | 
    
         
             
                  rescan_invalidate_tc(stratum, rescan, invalidate)
         
     | 
| 
       448 
451 
     | 
    
         
             
                end
         
     | 
| 
      
 452 
     | 
    
         
            +
             
     | 
| 
       449 
453 
     | 
    
         
             
                prune_rescan_invalidate(rescan, invalidate)
         
     | 
| 
       450 
454 
     | 
    
         
             
                # transitive closure
         
     | 
| 
       451 
455 
     | 
    
         
             
                @default_rescan = rescan.to_a
         
     | 
| 
       452 
456 
     | 
    
         
             
                @default_invalidate = invalidate.to_a
         
     | 
| 
       453 
457 
     | 
    
         | 
| 
       454 
     | 
    
         
            -
                # Now compute for each table that is to be scanned, the set of dependent 
     | 
| 
       455 
     | 
    
         
            -
                # if that table were to be 
     | 
| 
      
 458 
     | 
    
         
            +
                # Now compute for each table that is to be scanned, the set of dependent
         
     | 
| 
      
 459 
     | 
    
         
            +
                # tables and elements that will be invalidated if that table were to be
         
     | 
| 
      
 460 
     | 
    
         
            +
                # invalidated at run time.
         
     | 
| 
       456 
461 
     | 
    
         
             
                dflt_rescan = rescan
         
     | 
| 
       457 
462 
     | 
    
         
             
                dflt_invalidate = invalidate
         
     | 
| 
       458 
463 
     | 
    
         
             
                to_reset = rescan + invalidate
         
     | 
| 
       459 
464 
     | 
    
         
             
                num_strata.times do |stratum|
         
     | 
| 
       460 
465 
     | 
    
         
             
                  @scanners[stratum].each_value do |scanner|
         
     | 
| 
       461 
     | 
    
         
            -
                    # If it is going to be always invalidated, it doesn't need further 
     | 
| 
      
 466 
     | 
    
         
            +
                    # If it is going to be always invalidated, it doesn't need further
         
     | 
| 
      
 467 
     | 
    
         
            +
                    # examination
         
     | 
| 
       462 
468 
     | 
    
         
             
                    next if dflt_rescan.member? scanner
         
     | 
| 
       463 
469 
     | 
    
         | 
| 
       464 
470 
     | 
    
         
             
                    rescan = dflt_rescan + [scanner]  # add scanner to scan set
         
     | 
| 
         @@ -466,7 +472,8 @@ module Bud 
     | 
|
| 
       466 
472 
     | 
    
         
             
                    rescan_invalidate_tc(stratum, rescan, invalidate)
         
     | 
| 
       467 
473 
     | 
    
         
             
                    prune_rescan_invalidate(rescan, invalidate)
         
     | 
| 
       468 
474 
     | 
    
         
             
                    to_reset += rescan + invalidate
         
     | 
| 
       469 
     | 
    
         
            -
                    # Give the diffs (from default) to scanner; these are elements that are 
     | 
| 
      
 475 
     | 
    
         
            +
                    # Give the diffs (from default) to scanner; these are elements that are
         
     | 
| 
      
 476 
     | 
    
         
            +
                    # dependent on this scanner
         
     | 
| 
       470 
477 
     | 
    
         
             
                    diffscan = (rescan - dflt_rescan).find_all {|elem| elem.class <= PushElement}
         
     | 
| 
       471 
478 
     | 
    
         
             
                    scanner.invalidate_at_tick(diffscan, (invalidate - dflt_invalidate).to_a)
         
     | 
| 
       472 
479 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -474,13 +481,13 @@ module Bud 
     | 
|
| 
       474 
481 
     | 
    
         
             
                @reset_list = to_reset.to_a
         
     | 
| 
       475 
482 
     | 
    
         
             
              end
         
     | 
| 
       476 
483 
     | 
    
         | 
| 
       477 
     | 
    
         
            -
             
     | 
| 
       478 
     | 
    
         
            -
              #given rescan, invalidate sets, compute transitive closure
         
     | 
| 
      
 484 
     | 
    
         
            +
              # given rescan, invalidate sets, compute transitive closure
         
     | 
| 
       479 
485 
     | 
    
         
             
              def rescan_invalidate_tc(stratum, rescan, invalidate)
         
     | 
| 
       480 
486 
     | 
    
         
             
                rescan_len = rescan.size
         
     | 
| 
       481 
487 
     | 
    
         
             
                invalidate_len = invalidate.size
         
     | 
| 
       482 
488 
     | 
    
         
             
                while true
         
     | 
| 
       483 
     | 
    
         
            -
                  # Ask each element if it wants to add itself to either set, depending on 
     | 
| 
      
 489 
     | 
    
         
            +
                  # Ask each element if it wants to add itself to either set, depending on
         
     | 
| 
      
 490 
     | 
    
         
            +
                  # who else is in those sets already.
         
     | 
| 
       484 
491 
     | 
    
         
             
                  @push_sorted_elems[stratum].each {|t| t.add_rescan_invalidate(rescan, invalidate)}
         
     | 
| 
       485 
492 
     | 
    
         
             
                  break if rescan_len == rescan.size and invalidate_len == invalidate.size
         
     | 
| 
       486 
493 
     | 
    
         
             
                  rescan_len = rescan.size
         
     | 
| 
         @@ -533,10 +540,10 @@ module Bud 
     | 
|
| 
       533 
540 
     | 
    
         
             
                @rtracer.sleep if options[:rtrace]
         
     | 
| 
       534 
541 
     | 
    
         
             
              end
         
     | 
| 
       535 
542 
     | 
    
         | 
| 
       536 
     | 
    
         
            -
              # Startup a Bud instance 
     | 
| 
       537 
     | 
    
         
            -
              # UDP server socket. If do_tick is 
     | 
| 
       538 
     | 
    
         
            -
              #  
     | 
| 
       539 
     | 
    
         
            -
              #  
     | 
| 
      
 543 
     | 
    
         
            +
              # Startup a Bud instance if it is not currently started. This starts
         
     | 
| 
      
 544 
     | 
    
         
            +
              # EventMachine (if needed) and binds to a UDP server socket. If do_tick is
         
     | 
| 
      
 545 
     | 
    
         
            +
              # true, we also execute a single Bloom timestep. Regardless, calling this
         
     | 
| 
      
 546 
     | 
    
         
            +
              # method does NOT cause Bud to begin running asynchronously (see run_bg).
         
     | 
| 
       540 
547 
     | 
    
         
             
              def start(do_tick=false)
         
     | 
| 
       541 
548 
     | 
    
         
             
                start_reactor
         
     | 
| 
       542 
549 
     | 
    
         
             
                schedule_and_wait do
         
     | 
| 
         @@ -703,21 +710,26 @@ module Bud 
     | 
|
| 
       703 
710 
     | 
    
         
             
              # Note that registering callbacks on persistent collections (e.g., tables,
         
     | 
| 
       704 
711 
     | 
    
         
             
              # syncs and stores) is probably not wise: as long as any tuples are stored in
         
     | 
| 
       705 
712 
     | 
    
         
             
              # the collection, the callback will be invoked at the end of every tick.
         
     | 
| 
       706 
     | 
    
         
            -
              def register_callback(tbl_name, & 
     | 
| 
      
 713 
     | 
    
         
            +
              def register_callback(tbl_name, &blk)
         
     | 
| 
       707 
714 
     | 
    
         
             
                # We allow callbacks to be added before or after EM has been started. To
         
     | 
| 
       708 
715 
     | 
    
         
             
                # simplify matters, we start EM if it hasn't been started yet.
         
     | 
| 
       709 
716 
     | 
    
         
             
                start_reactor
         
     | 
| 
       710 
717 
     | 
    
         
             
                cb_id = nil
         
     | 
| 
       711 
718 
     | 
    
         
             
                schedule_and_wait do
         
     | 
| 
       712 
     | 
    
         
            -
                   
     | 
| 
       713 
     | 
    
         
            -
             
     | 
| 
       714 
     | 
    
         
            -
             
     | 
| 
      
 719 
     | 
    
         
            +
                  cb_id = do_register_callback(tbl_name, &blk)
         
     | 
| 
      
 720 
     | 
    
         
            +
                end
         
     | 
| 
      
 721 
     | 
    
         
            +
                return cb_id
         
     | 
| 
      
 722 
     | 
    
         
            +
              end
         
     | 
| 
       715 
723 
     | 
    
         | 
| 
       716 
     | 
    
         
            -
             
     | 
| 
       717 
     | 
    
         
            -
             
     | 
| 
       718 
     | 
    
         
            -
                   
     | 
| 
       719 
     | 
    
         
            -
                  @callback_id += 1
         
     | 
| 
      
 724 
     | 
    
         
            +
              def do_register_callback(tbl_name, &blk)
         
     | 
| 
      
 725 
     | 
    
         
            +
                unless @tables.has_key? tbl_name
         
     | 
| 
      
 726 
     | 
    
         
            +
                  raise Bud::Error, "no such collection: #{tbl_name}"
         
     | 
| 
       720 
727 
     | 
    
         
             
                end
         
     | 
| 
      
 728 
     | 
    
         
            +
             
     | 
| 
      
 729 
     | 
    
         
            +
                raise Bud::Error if @callbacks.has_key? @callback_id
         
     | 
| 
      
 730 
     | 
    
         
            +
                @callbacks[@callback_id] = [tbl_name, blk]
         
     | 
| 
      
 731 
     | 
    
         
            +
                cb_id = @callback_id
         
     | 
| 
      
 732 
     | 
    
         
            +
                @callback_id += 1
         
     | 
| 
       721 
733 
     | 
    
         
             
                return cb_id
         
     | 
| 
       722 
734 
     | 
    
         
             
              end
         
     | 
| 
       723 
735 
     | 
    
         | 
| 
         @@ -735,10 +747,6 @@ module Bud 
     | 
|
| 
       735 
747 
     | 
    
         
             
              # inserted into the output collection: these are returned to the caller.
         
     | 
| 
       736 
748 
     | 
    
         
             
              def sync_callback(in_tbl, tupleset, out_tbl)
         
     | 
| 
       737 
749 
     | 
    
         
             
                q = Queue.new
         
     | 
| 
       738 
     | 
    
         
            -
                # XXXX Why two separate entrances into the event loop? Why is the callback not registered in the sync_do below?
         
     | 
| 
       739 
     | 
    
         
            -
                cb = register_callback(out_tbl) do |c|
         
     | 
| 
       740 
     | 
    
         
            -
                  q.push c.to_a
         
     | 
| 
       741 
     | 
    
         
            -
                end
         
     | 
| 
       742 
750 
     | 
    
         | 
| 
       743 
751 
     | 
    
         
             
                # If the runtime shuts down before we see anything in the output collection,
         
     | 
| 
       744 
752 
     | 
    
         
             
                # make sure we hear about it so we can raise an error
         
     | 
| 
         @@ -747,16 +755,21 @@ module Bud 
     | 
|
| 
       747 
755 
     | 
    
         
             
                  q.push :callback
         
     | 
| 
       748 
756 
     | 
    
         
             
                end
         
     | 
| 
       749 
757 
     | 
    
         | 
| 
       750 
     | 
    
         
            -
                 
     | 
| 
       751 
     | 
    
         
            -
             
     | 
| 
      
 758 
     | 
    
         
            +
                cb = nil
         
     | 
| 
      
 759 
     | 
    
         
            +
                sync_do {
         
     | 
| 
      
 760 
     | 
    
         
            +
                  if in_tbl
         
     | 
| 
       752 
761 
     | 
    
         
             
                    t = @tables[in_tbl]
         
     | 
| 
       753 
762 
     | 
    
         
             
                    if t.class <= Bud::BudChannel or t.class <= Bud::BudZkTable
         
     | 
| 
       754 
763 
     | 
    
         
             
                      t <~ tupleset
         
     | 
| 
       755 
764 
     | 
    
         
             
                    else
         
     | 
| 
       756 
765 
     | 
    
         
             
                      t <+ tupleset
         
     | 
| 
       757 
766 
     | 
    
         
             
                    end
         
     | 
| 
       758 
     | 
    
         
            -
                   
     | 
| 
       759 
     | 
    
         
            -
             
     | 
| 
      
 767 
     | 
    
         
            +
                  end
         
     | 
| 
      
 768 
     | 
    
         
            +
             
     | 
| 
      
 769 
     | 
    
         
            +
                  cb = do_register_callback(out_tbl) do |c|
         
     | 
| 
      
 770 
     | 
    
         
            +
                    q.push c.to_a
         
     | 
| 
      
 771 
     | 
    
         
            +
                  end
         
     | 
| 
      
 772 
     | 
    
         
            +
                }
         
     | 
| 
       760 
773 
     | 
    
         
             
                result = q.pop
         
     | 
| 
       761 
774 
     | 
    
         
             
                if result == :callback
         
     | 
| 
       762 
775 
     | 
    
         
             
                  # Don't try to unregister the callbacks first: runtime is already shutdown
         
     | 
| 
         @@ -764,7 +777,7 @@ module Bud 
     | 
|
| 
       764 
777 
     | 
    
         
             
                end
         
     | 
| 
       765 
778 
     | 
    
         
             
                unregister_callback(cb)
         
     | 
| 
       766 
779 
     | 
    
         
             
                cancel_shutdown_cb(shutdown_cb)
         
     | 
| 
       767 
     | 
    
         
            -
                return  
     | 
| 
      
 780 
     | 
    
         
            +
                return result
         
     | 
| 
       768 
781 
     | 
    
         
             
              end
         
     | 
| 
       769 
782 
     | 
    
         | 
| 
       770 
783 
     | 
    
         
             
              # A common special case for sync_callback: block on a delta to a table.
         
     | 
| 
         @@ -848,7 +861,7 @@ module Bud 
     | 
|
| 
       848 
861 
     | 
    
         
             
                # instance that hasn't been started yet.
         
     | 
| 
       849 
862 
     | 
    
         
             
                return if @instance_id == ILLEGAL_INSTANCE_ID
         
     | 
| 
       850 
863 
     | 
    
         
             
                $signal_lock.synchronize {
         
     | 
| 
       851 
     | 
    
         
            -
                  raise unless $bud_instances.has_key? @instance_id
         
     | 
| 
      
 864 
     | 
    
         
            +
                  raise Bud::Error unless $bud_instances.has_key? @instance_id
         
     | 
| 
       852 
865 
     | 
    
         
             
                  $bud_instances.delete @instance_id
         
     | 
| 
       853 
866 
     | 
    
         
             
                  @instance_id = ILLEGAL_INSTANCE_ID
         
     | 
| 
       854 
867 
     | 
    
         
             
                }
         
     | 
| 
         @@ -911,6 +924,28 @@ module Bud 
     | 
|
| 
       911 
924 
     | 
    
         
             
                start(true)
         
     | 
| 
       912 
925 
     | 
    
         
             
              end
         
     | 
| 
       913 
926 
     | 
    
         | 
| 
      
 927 
     | 
    
         
            +
              # Evaluate all bootstrap blocks and tick deltas
         
     | 
| 
      
 928 
     | 
    
         
            +
              def do_bootstrap
         
     | 
| 
      
 929 
     | 
    
         
            +
                # Evaluate bootstrap for imported modules
         
     | 
| 
      
 930 
     | 
    
         
            +
                @this_rule_context = self
         
     | 
| 
      
 931 
     | 
    
         
            +
                imported = import_defs.keys
         
     | 
| 
      
 932 
     | 
    
         
            +
                imported.each do |mod_alias|
         
     | 
| 
      
 933 
     | 
    
         
            +
                  wrapper = import_instance mod_alias
         
     | 
| 
      
 934 
     | 
    
         
            +
                  wrapper.do_bootstrap
         
     | 
| 
      
 935 
     | 
    
         
            +
                end
         
     | 
| 
      
 936 
     | 
    
         
            +
                self.class.ancestors.reverse.each do |anc|
         
     | 
| 
      
 937 
     | 
    
         
            +
                  anc.instance_methods(false).each do |m|
         
     | 
| 
      
 938 
     | 
    
         
            +
                    if /^__bootstrap__/.match m
         
     | 
| 
      
 939 
     | 
    
         
            +
                      self.method(m.to_sym).call
         
     | 
| 
      
 940 
     | 
    
         
            +
                    end
         
     | 
| 
      
 941 
     | 
    
         
            +
                  end
         
     | 
| 
      
 942 
     | 
    
         
            +
                end
         
     | 
| 
      
 943 
     | 
    
         
            +
                bootstrap
         
     | 
| 
      
 944 
     | 
    
         
            +
             
     | 
| 
      
 945 
     | 
    
         
            +
                @tables.each_value {|t| t.bootstrap} if toplevel == self
         
     | 
| 
      
 946 
     | 
    
         
            +
                @done_bootstrap = true
         
     | 
| 
      
 947 
     | 
    
         
            +
              end
         
     | 
| 
      
 948 
     | 
    
         
            +
             
     | 
| 
       914 
949 
     | 
    
         
             
              # One timestep of Bloom execution. This MUST be invoked from the EventMachine
         
     | 
| 
       915 
950 
     | 
    
         
             
              # thread; it is not intended to be called directly by client code.
         
     | 
| 
       916 
951 
     | 
    
         
             
              def tick_internal
         
     | 
| 
         @@ -919,10 +954,11 @@ module Bud 
     | 
|
| 
       919 
954 
     | 
    
         
             
                  starttime = Time.now if options[:metrics]
         
     | 
| 
       920 
955 
     | 
    
         
             
                  if options[:metrics] and not @endtime.nil?
         
     | 
| 
       921 
956 
     | 
    
         
             
                    @metrics[:betweentickstats] ||= initialize_stats
         
     | 
| 
       922 
     | 
    
         
            -
                    @metrics[:betweentickstats] = running_stats(@metrics[:betweentickstats], 
     | 
| 
      
 957 
     | 
    
         
            +
                    @metrics[:betweentickstats] = running_stats(@metrics[:betweentickstats],
         
     | 
| 
      
 958 
     | 
    
         
            +
                                                                starttime - @endtime)
         
     | 
| 
       923 
959 
     | 
    
         
             
                  end
         
     | 
| 
       924 
960 
     | 
    
         
             
                  @inside_tick = true
         
     | 
| 
       925 
     | 
    
         
            -
             
     | 
| 
      
 961 
     | 
    
         
            +
             
     | 
| 
       926 
962 
     | 
    
         
             
                  unless @done_bootstrap
         
     | 
| 
       927 
963 
     | 
    
         
             
                    do_bootstrap
         
     | 
| 
       928 
964 
     | 
    
         
             
                    do_wiring
         
     | 
| 
         @@ -932,27 +968,28 @@ module Bud 
     | 
|
| 
       932 
968 
     | 
    
         
             
                    @default_rescan.each {|elem| elem.rescan = true}
         
     | 
| 
       933 
969 
     | 
    
         
             
                    @default_invalidate.each {|elem|
         
     | 
| 
       934 
970 
     | 
    
         
             
                      elem.invalidated = true
         
     | 
| 
       935 
     | 
    
         
            -
                       
     | 
| 
      
 971 
     | 
    
         
            +
                      # Call tick on tables here itself. The rest below
         
     | 
| 
      
 972 
     | 
    
         
            +
                      elem.invalidate_cache unless elem.class <= PushElement
         
     | 
| 
       936 
973 
     | 
    
         
             
                    }
         
     | 
| 
       937 
974 
     | 
    
         | 
| 
       938 
975 
     | 
    
         
             
                    num_strata = @push_sorted_elems.size
         
     | 
| 
       939 
     | 
    
         
            -
                    # The following loop invalidates additional (non-default) elements and 
     | 
| 
       940 
     | 
    
         
            -
                    # invalidation state of a table.
         
     | 
| 
      
 976 
     | 
    
         
            +
                    # The following loop invalidates additional (non-default) elements and
         
     | 
| 
      
 977 
     | 
    
         
            +
                    # tables that depend on the run-time invalidation state of a table.
         
     | 
| 
       941 
978 
     | 
    
         
             
                    # Loop once to set the flags
         
     | 
| 
       942 
979 
     | 
    
         
             
                    num_strata.times do |stratum|
         
     | 
| 
       943 
980 
     | 
    
         
             
                      @scanners[stratum].each_value do |scanner|
         
     | 
| 
       944 
981 
     | 
    
         
             
                        if scanner.rescan
         
     | 
| 
       945 
982 
     | 
    
         
             
                          scanner.rescan_set.each {|e| e.rescan = true}
         
     | 
| 
       946 
983 
     | 
    
         
             
                          scanner.invalidate_set.each {|e|
         
     | 
| 
       947 
     | 
    
         
            -
                            e.invalidated = true 
     | 
| 
      
 984 
     | 
    
         
            +
                            e.invalidated = true
         
     | 
| 
       948 
985 
     | 
    
         
             
                            e.invalidate_cache unless e.class <= PushElement
         
     | 
| 
       949 
     | 
    
         
            -
             
     | 
| 
      
 986 
     | 
    
         
            +
                          }
         
     | 
| 
       950 
987 
     | 
    
         
             
                        end
         
     | 
| 
       951 
988 
     | 
    
         
             
                      end
         
     | 
| 
       952 
989 
     | 
    
         
             
                    end
         
     | 
| 
       953 
     | 
    
         
            -
                    #Loop a second time to actually call invalidate_cache
         
     | 
| 
      
 990 
     | 
    
         
            +
                    # Loop a second time to actually call invalidate_cache
         
     | 
| 
       954 
991 
     | 
    
         
             
                    num_strata.times do |stratum|
         
     | 
| 
       955 
     | 
    
         
            -
                      @push_sorted_elems[stratum].each { 
     | 
| 
      
 992 
     | 
    
         
            +
                      @push_sorted_elems[stratum].each {|e| e.invalidate_cache if e.invalidated}
         
     | 
| 
       956 
993 
     | 
    
         
             
                    end
         
     | 
| 
       957 
994 
     | 
    
         
             
                  end
         
     | 
| 
       958 
995 
     | 
    
         | 
| 
         @@ -970,20 +1007,20 @@ module Bud 
     | 
|
| 
       970 
1007 
     | 
    
         
             
                      # tick deltas on any merge targets and look for more deltas
         
     | 
| 
       971 
1008 
     | 
    
         
             
                      # check to see if any joins saw a delta
         
     | 
| 
       972 
1009 
     | 
    
         
             
                      push_joins[stratum].each do |p|
         
     | 
| 
       973 
     | 
    
         
            -
                        if p.found_delta 
     | 
| 
       974 
     | 
    
         
            -
                          fixpoint = false 
     | 
| 
      
 1010 
     | 
    
         
            +
                        if p.found_delta
         
     | 
| 
      
 1011 
     | 
    
         
            +
                          fixpoint = false
         
     | 
| 
       975 
1012 
     | 
    
         
             
                          p.tick_deltas
         
     | 
| 
       976 
1013 
     | 
    
         
             
                        end
         
     | 
| 
       977 
1014 
     | 
    
         
             
                      end
         
     | 
| 
       978 
     | 
    
         
            -
                      merge_targets[stratum]. 
     | 
| 
      
 1015 
     | 
    
         
            +
                      merge_targets[stratum].each do |t|
         
     | 
| 
       979 
1016 
     | 
    
         
             
                        fixpoint = false if t.tick_deltas
         
     | 
| 
       980 
1017 
     | 
    
         
             
                      end
         
     | 
| 
       981 
1018 
     | 
    
         
             
                    end
         
     | 
| 
       982 
1019 
     | 
    
         
             
                    # push end-of-fixpoint
         
     | 
| 
       983 
     | 
    
         
            -
                    @push_sorted_elems[stratum].each  
     | 
| 
      
 1020 
     | 
    
         
            +
                    @push_sorted_elems[stratum].each do |p|
         
     | 
| 
       984 
1021 
     | 
    
         
             
                      p.stratum_end
         
     | 
| 
       985 
     | 
    
         
            -
                     
     | 
| 
       986 
     | 
    
         
            -
                    merge_targets[stratum]. 
     | 
| 
      
 1022 
     | 
    
         
            +
                    end
         
     | 
| 
      
 1023 
     | 
    
         
            +
                    merge_targets[stratum].each do |t|
         
     | 
| 
       987 
1024 
     | 
    
         
             
                      t.flush_deltas
         
     | 
| 
       988 
1025 
     | 
    
         
             
                    end
         
     | 
| 
       989 
1026 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -1019,7 +1056,7 @@ module Bud 
     | 
|
| 
       1019 
1056 
     | 
    
         | 
| 
       1020 
1057 
     | 
    
         
             
              private
         
     | 
| 
       1021 
1058 
     | 
    
         | 
| 
       1022 
     | 
    
         
            -
              # Builtin  
     | 
| 
      
 1059 
     | 
    
         
            +
              # Builtin Bud state (predefined collections). We could define this using the
         
     | 
| 
       1023 
1060 
     | 
    
         
             
              # standard state block syntax, but we want to ensure that builtin state is
         
     | 
| 
       1024 
1061 
     | 
    
         
             
              # initialized before user-defined state.
         
     | 
| 
       1025 
1062 
     | 
    
         
             
              def builtin_state
         
     | 
| 
         @@ -1028,12 +1065,11 @@ module Bud 
     | 
|
| 
       1028 
1065 
     | 
    
         | 
| 
       1029 
1066 
     | 
    
         
             
                loopback  :localtick, [:col1]
         
     | 
| 
       1030 
1067 
     | 
    
         
             
                @stdio = terminal :stdio
         
     | 
| 
       1031 
     | 
    
         
            -
                signal :signals, [:name]
         
     | 
| 
       1032 
1068 
     | 
    
         
             
                scratch :halt, [:key]
         
     | 
| 
       1033 
1069 
     | 
    
         
             
                @periodics = table :periodics_tbl, [:pername] => [:period]
         
     | 
| 
       1034 
1070 
     | 
    
         | 
| 
       1035 
1071 
     | 
    
         
             
                # for BUD reflection
         
     | 
| 
       1036 
     | 
    
         
            -
                table :t_rules, [:bud_obj, :rule_id] => [:lhs, :op, :src, :orig_src]
         
     | 
| 
      
 1072 
     | 
    
         
            +
                table :t_rules, [:bud_obj, :rule_id] => [:lhs, :op, :src, :orig_src, :nm_funcs_called]
         
     | 
| 
       1037 
1073 
     | 
    
         
             
                table :t_depends, [:bud_obj, :rule_id, :lhs, :op, :body] => [:nm]
         
     | 
| 
       1038 
1074 
     | 
    
         
             
                table :t_provides, [:interface] => [:input]
         
     | 
| 
       1039 
1075 
     | 
    
         
             
                table :t_underspecified, t_provides.schema
         
     | 
| 
         @@ -1072,10 +1108,10 @@ module Bud 
     | 
|
| 
       1072 
1108 
     | 
    
         
             
              end
         
     | 
| 
       1073 
1109 
     | 
    
         | 
| 
       1074 
1110 
     | 
    
         
             
              def eval_rules(rules, strat_num)
         
     | 
| 
       1075 
     | 
    
         
            -
                # This routine evals the rules in a given stratum, which results in a wiring 
     | 
| 
       1076 
     | 
    
         
            -
                 
     | 
| 
      
 1111 
     | 
    
         
            +
                # This routine evals the rules in a given stratum, which results in a wiring
         
     | 
| 
      
 1112 
     | 
    
         
            +
                # of PushElements
         
     | 
| 
      
 1113 
     | 
    
         
            +
                @this_stratum = strat_num
         
     | 
| 
       1077 
1114 
     | 
    
         
             
                rules.each_with_index do |rule, i|
         
     | 
| 
       1078 
     | 
    
         
            -
                  @this_rule = i
         
     | 
| 
       1079 
1115 
     | 
    
         
             
                  @this_rule_context = rule.bud_obj # user-supplied code blocks will be evaluated in this context at run-time
         
     | 
| 
       1080 
1116 
     | 
    
         
             
                  begin
         
     | 
| 
       1081 
1117 
     | 
    
         
             
                    eval_rule(rule.bud_obj, rule.src)
         
     | 
| 
         @@ -1123,7 +1159,7 @@ module Bud 
     | 
|
| 
       1123 
1159 
     | 
    
         
             
                  Bud.shutdown_all_instances(false)
         
     | 
| 
       1124 
1160 
     | 
    
         | 
| 
       1125 
1161 
     | 
    
         
             
                  $got_shutdown_signal = false
         
     | 
| 
       1126 
     | 
    
         
            -
                  $ 
     | 
| 
      
 1162 
     | 
    
         
            +
                  $signal_handler_setup = false
         
     | 
| 
       1127 
1163 
     | 
    
         | 
| 
       1128 
1164 
     | 
    
         
             
                  yield
         
     | 
| 
       1129 
1165 
     | 
    
         
             
                end
         
     | 
| 
         @@ -1146,15 +1182,11 @@ module Bud 
     | 
|
| 
       1146 
1182 
     | 
    
         
             
                $signal_lock.synchronize {
         
     | 
| 
       1147 
1183 
     | 
    
         
             
                  # If we setup signal handlers and then fork a new process, we want to
         
     | 
| 
       1148 
1184 
     | 
    
         
             
                  # reinitialize the signal handler in the child process.
         
     | 
| 
       1149 
     | 
    
         
            -
                  unless b.options[:signal_handling] == :none  
     | 
| 
      
 1185 
     | 
    
         
            +
                  unless b.options[:signal_handling] == :none || $signal_handler_setup
         
     | 
| 
       1150 
1186 
     | 
    
         
             
                    EventMachine::PeriodicTimer.new(SIGNAL_CHECK_PERIOD) do
         
     | 
| 
       1151 
1187 
     | 
    
         
             
                      if $got_shutdown_signal
         
     | 
| 
       1152 
     | 
    
         
            -
                         
     | 
| 
       1153 
     | 
    
         
            -
             
     | 
| 
       1154 
     | 
    
         
            -
                        else
         
     | 
| 
       1155 
     | 
    
         
            -
                          Bud.shutdown_all_instances
         
     | 
| 
       1156 
     | 
    
         
            -
                          Bud.stop_em_loop
         
     | 
| 
       1157 
     | 
    
         
            -
                        end
         
     | 
| 
      
 1188 
     | 
    
         
            +
                        Bud.shutdown_all_instances
         
     | 
| 
      
 1189 
     | 
    
         
            +
                        Bud.stop_em_loop
         
     | 
| 
       1158 
1190 
     | 
    
         
             
                        $got_shutdown_signal = false
         
     | 
| 
       1159 
1191 
     | 
    
         
             
                      end
         
     | 
| 
       1160 
1192 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -1162,10 +1194,9 @@ module Bud 
     | 
|
| 
       1162 
1194 
     | 
    
         
             
                    ["INT", "TERM"].each do |signal|
         
     | 
| 
       1163 
1195 
     | 
    
         
             
                      Signal.trap(signal) {
         
     | 
| 
       1164 
1196 
     | 
    
         
             
                        $got_shutdown_signal = true
         
     | 
| 
       1165 
     | 
    
         
            -
                        b.sync_do{b.signals.pending_merge([[signal]])}
         
     | 
| 
       1166 
1197 
     | 
    
         
             
                      }
         
     | 
| 
       1167 
1198 
     | 
    
         
             
                    end
         
     | 
| 
       1168 
     | 
    
         
            -
                    $ 
     | 
| 
      
 1199 
     | 
    
         
            +
                    $signal_handler_setup = true
         
     | 
| 
       1169 
1200 
     | 
    
         
             
                  end
         
     | 
| 
       1170 
1201 
     | 
    
         | 
| 
       1171 
1202 
     | 
    
         
             
                  $instance_id += 1
         
     | 
| 
         @@ -1174,15 +1205,6 @@ module Bud 
     | 
|
| 
       1174 
1205 
     | 
    
         
             
                }
         
     | 
| 
       1175 
1206 
     | 
    
         
             
              end
         
     | 
| 
       1176 
1207 
     | 
    
         | 
| 
       1177 
     | 
    
         
            -
              def self.tick_all_instances
         
     | 
| 
       1178 
     | 
    
         
            -
                instances = nil
         
     | 
| 
       1179 
     | 
    
         
            -
                $signal_lock.synchronize {
         
     | 
| 
       1180 
     | 
    
         
            -
                  instances = $bud_instances.clone
         
     | 
| 
       1181 
     | 
    
         
            -
                }
         
     | 
| 
       1182 
     | 
    
         
            -
             
     | 
| 
       1183 
     | 
    
         
            -
                instances.each_value {|b| b.sync_do }
         
     | 
| 
       1184 
     | 
    
         
            -
              end
         
     | 
| 
       1185 
     | 
    
         
            -
             
     | 
| 
       1186 
1208 
     | 
    
         
             
              def self.shutdown_all_instances(do_shutdown_cb=true)
         
     | 
| 
       1187 
1209 
     | 
    
         
             
                instances = nil
         
     | 
| 
       1188 
1210 
     | 
    
         
             
                $signal_lock.synchronize {
         
     |