Author: sitharus
Date: 2006-06-10 08:12:10 +0000 (Sat, 10 Jun 2006)
New Revision: 9122

Added:
   trunk/apps/rubyFreenet/rsite/createsite
   trunk/apps/rubyFreenet/rsite/updatesites
Modified:
   trunk/apps/rubyFreenet/freenet/fcp/client.rb
   trunk/apps/rubyFreenet/freenet/fcp/message.rb
   trunk/apps/rubyFreenet/freenet/uri.rb
   trunk/apps/rubyFreenet/rsite/rsite.rb
Log:
Tidied up Mutex use to stop excessive CPU use. Performance will suffer slightly.
Added some scripts to show freesite management and insertion


Modified: trunk/apps/rubyFreenet/freenet/fcp/client.rb
===================================================================
--- trunk/apps/rubyFreenet/freenet/fcp/client.rb        2006-06-10 03:18:04 UTC 
(rev 9121)
+++ trunk/apps/rubyFreenet/freenet/fcp/client.rb        2006-06-10 08:12:10 UTC 
(rev 9122)
@@ -87,7 +87,6 @@
         @messages = {}
         @running = false
         @lock = Mutex.new
-        @logger_mutex = Mutex.new
         @logger = Logger.new('FCPClientLog','daily')
         @message_queue = Queue.new
         connect
@@ -139,6 +138,7 @@
         if async
           message
         else
+          message.wait_for_response
           [message.response.items['InsertURI'], 
message.response.items['RequestURI']]
         end
       end
@@ -189,12 +189,13 @@
           message
         else
           loop do
+            message.wait_for_response
             message.lock
             case message.response.type
             when 'AllData'
               message.unlock
               return message.response
-            when 'GetFailed'
+            when 'GetFailed','ProtocolError'
               message.unlock
               raise RequestFailed.new(message.response)
             else
@@ -242,12 +243,13 @@
           message
         else
           loop do
+            message.wait_for_response
             message.lock
             case message.response.type
             when 'PutSuccessful'
               message.unlock
               return message.response.items['URI']
-            when 'PutFailed'
+            when 'PutFailed','ProtocolError'
               message.unlock
               raise RequestFailed.new(message.response)
             else
@@ -276,12 +278,13 @@
           message
         else
           loop do
+            message.wait_for_response
             message.lock
             case message.response.type
             when 'PutSuccessful'
               message.unlock
               return message.response.items['URI']
-            when 'PutFailed'
+            when 'PutFailed','ProtocolError'
               message.unlock
               raise RequestFailed.new(message.response)
             else
@@ -298,6 +301,7 @@
         log(DEBUG, 'Requesting status')
         message = Message.new('GetRequestStatus', nil, 
'Identifier'=>identifier, 'Global'=>global)
         send(message, async)
+        message.wait_for_response
       end

       private
@@ -306,15 +310,16 @@
         log(DEBUG, 'Sending ClientHello')
         message = Message.new('ClientHello', nil, 'Name'=>@client_name, 
'ExpectedVersion'=>'2.0')
         send(message)
+        message.wait_for_response
         log(DEBUG, "Got NodeHello - Freenet 
#{message.response.items['Version']}")
         if message.response.items['Testnet'] == 'true'
           log(WARN, "Connected to Testnet, you have no anonymity!")
         end
       end

-      # Logger utility method
+      # Logger utility method. Logger should be thread-safe
       def log(severity, message)
-        @logger_mutex.synchronize {@logger.add(severity, message)}
+        @logger.add(severity, message)
       end

       # Queue the message for sending by the worker thread.
@@ -324,9 +329,7 @@
       def send(message, asynchronous = false)
         log(DEBUG, "Queuing #{message.type} - #{message.identifier}")
         @message_queue << message
-        unless asynchronous
-          return message.wait_for_response
-        end
+        message
       end

       # Worker thread, loops every second or so and checks for new messages to 
send then messages
@@ -334,20 +337,29 @@
       def socket_thread
         @callback_threads = []
         loop do
+          # Join threads. Wait 0.1 seconds for each thread.
           @callback_threads.each do |thread|
             begin
-              thread.join(0.1)
+              @callback_threads.delete(thread) if thread.join(0.1)
             rescue RequestFinished=>e
               @messages.delete(e.message)
             rescue Exception=>e
               puts "Callback exception #{e}"
             end
           end
-          @lock.synchronize do
-            if @running == false
-              @socket.close
-              Thread.exit
+          
+          @messages.each do |k, m|
+            begin
+              m.try_lock
+            rescue
             end
+           end
+          
+          # I'm assuming that setting @running is atomic. There shouldn't be a 
race
+          # condition here as this thread will never set @running.
+          if @running == false
+            @socket.close
+            Thread.exit
           end
           begin
             while message = @message_queue.pop(true)
@@ -355,8 +367,9 @@
             end
           rescue ThreadError => e
           end
-
-          if select([@socket], nil, nil, 1)
+          
+          # Wait two seconds for communication, shouldn't slow down too much 
and should save CPU.
+          if select([@socket], nil, nil, 2)
             message = read_message
             dispatch_message(message)
           end
@@ -375,6 +388,7 @@
         if message.identifier
           original_message = @messages[message.identifier]
           if original_message
+            original_message.unlock
             original_message.reply = message
             thread = Thread.new do
               original_message.lock
@@ -449,6 +463,7 @@
         raise FCPConnectionError.new('Socket does not exist') unless @socket
         log(DEBUG, "Sending #{message.type} #{message.identifier}")
         @messages[message.identifier] ||= message
+        message.lock
         unless message.load_only
           log(DEBUG, "W: #{message.type}")
           @socket.write(message.type+"\n")

Modified: trunk/apps/rubyFreenet/freenet/fcp/message.rb
===================================================================
--- trunk/apps/rubyFreenet/freenet/fcp/message.rb       2006-06-10 03:18:04 UTC 
(rev 9121)
+++ trunk/apps/rubyFreenet/freenet/fcp/message.rb       2006-06-10 08:12:10 UTC 
(rev 9122)
@@ -36,11 +36,19 @@
       end

       # Lock the object. Call before using in async situations
-      def lock; @mutex.lock; end
+      def lock(delay = 5)
+        until @mutex.try_lock
+          sleep(delay)
+        end
+      end

       # Unlock. Call after using asychronously
       def unlock; @mutex.unlock; end

+      def locked?; @mutex.locked?; end
+        
+      def try_lock; @mutex.try_lock; end
+      
       # Dispatch the callback. Private to FCP::Client
       def callback(status)
         @callback.call(status, self, @response) unless @callback.nil?
@@ -59,10 +67,10 @@
       # called from any thread.
       def wait_for_response
         until @response
-          sleep(3)
+          sleep(5)
+          next if locked?
           lock
           unlock
-          Thread.pass
         end
       end
     end

Modified: trunk/apps/rubyFreenet/freenet/uri.rb
===================================================================
--- trunk/apps/rubyFreenet/freenet/uri.rb       2006-06-10 03:18:04 UTC (rev 
9121)
+++ trunk/apps/rubyFreenet/freenet/uri.rb       2006-06-10 08:12:10 UTC (rev 
9122)
@@ -5,7 +5,7 @@
   # This could be completely wrong. Any criticism welcome
   class URI
     include Comparable
-    attr_accessor :type, :site, :path, :uri, :version
+    attr_accessor :type, :site, :name, :path, :uri, :version

     # This can take a URI in following formats:
     #  /freenet:SSK at ...
@@ -25,14 +25,40 @@
       @uri = uri
       @type = @uri.match(/^(?:[UKS]S|CH)K/)[0]
       @site = @uri.match(/^(?:[UKS]S|CH)K@([^\/]+)/)[1]
-      @path = @uri.match(/(\/[^#\?]+)/)[1] if @uri =~ /\/[^#\?]+/
+      case @type
+      when 'KSK', 'CHK'
+        @path = @uri.match(/(\/[^#\?]+)/)[1] if @uri =~ /\/[^#\?]+/
+      when 'SSK'
+        path = @uri.match(/(\/[^#\?]+)/)[1] if @uri =~ /\/[^#\?]+/
+        if path
+          parts = @uri.match(%r{(/[^/]+?)(?:-([0-9]+))?/(.*)})
+          @name = parts[1]
+          @version = parts[2]
+          @path = parts[3]
+        end
+      when 'USK'
+        path = @uri.match(/(\/[^#\?]+)/)[1] if @uri =~ /\/[^#\?]+/
+        if path
+          parts = @uri.match(%r{(/[^/]+?)(?:[/-]([0-9]+))?/(.*)})
+          @name = parts[1]
+          @version = parts[2]
+          @path = parts[3]
+        end
+      end
       @anchor = @uri.match(/#(.+)/)[1] if @uri =~ /#.+/
       @query_string = @uri.match(/\?([^#]+)/)[1] if @uri =~ /\?/
     end

     # Return a URI that can be fed to Freenet
     def uri
-      "#{@type}@#{@site}#{@path}#{'/'+ at version.to_s if 
@version}#{"?#{@query_string}" if @query_string}#{"##{@anchor}" if @anchor}"
+      case @type
+      when 'KSK','CHK'
+        "#{@type}@#{@site}#{@path}"
+      when 'USK'
+        "#{@type}@#{@site}#{@path}#{'/'+ at version.to_s if 
@version}#{"?#{@query_string}" if @query_string}#{"##{@anchor}" if @anchor}"
+      when 'SSK'
+        "#{@type}@#{@site}#{@path}#{'-'+ at version.to_s if 
@version}#{"?#{@query_string}" if @query_string}#{"##{@anchor}" if @anchor}"
+      end
     end

     # Merge in a URI or a URI fragment and provide the finished URI. Attempts
@@ -57,7 +83,7 @@

     # Returns true if we're at the 'base' page of a URI using the following:
     # - KSKs and CHKs only have one file, so always the base
-    # - SSKs have a 'base', eg SSK at .../mysite/
+    # - SSKs have a 'base', eg SSK at .../mysite-2/
     # - USKs start with /sitename and end with /revision or  -revision, though 
the latter
     #   is technically wrong.
     #

Added: trunk/apps/rubyFreenet/rsite/createsite
===================================================================
--- trunk/apps/rubyFreenet/rsite/createsite     2006-06-10 03:18:04 UTC (rev 
9121)
+++ trunk/apps/rubyFreenet/rsite/createsite     2006-06-10 08:12:10 UTC (rev 
9122)
@@ -0,0 +1,79 @@
+#!/usr/bin/ruby
+# == Create Site
+#
+# Creates a new freesite to be managed by rsite
+#
+# === Usage
+#
+# ./createsite [OPTIONS] ... SITE_DIRECTORY
+#
+# All arguments except SITE_DIRECTORY are optional
+#
+# -h, --help:
+#     Display this message
+#
+# -n, --name:
+#     The name of the freesite, used in the URL, such as SSK at .../name/, 
defaults to the end of SITE_DIRECTORY.
+#     Stick to a-zA-Z0-9_- for characters
+#
+# -t, --type:
+#     The type of site to insert, either USK or SSK, defaults to USK
+#
+# -k, --keys:
+#     The keys if already generated, in the format [INSERT_KEY;REQUEST_KEY], 
without the SSK@ bit
+#
+# -v, --version:
+#     The initial version of the site to insert, defaults to 1
+#
+# SITE_DIRECTORY is the path to the base of the site to insert, eg, ~/my_flog
+
+require 'rsite'
+require 'getoptlong'
+require 'rdoc/usage'
+
+opts = GetoptLong.new(
+  ['--type', '-t', GetoptLong::REQUIRED_ARGUMENT],
+  ['--name', '-n', GetoptLong::REQUIRED_ARGUMENT],
+  ['--help', '-h', GetoptLong::NO_ARGUMENT],
+  ['--keys', '-k', GetoptLong::REQUIRED_ARGUMENT],
+  ['--version', '-v', GetoptLong::REQUIRED_ARGUMENT]
+)
+
+type = 'USK'
+keys = nil
+version = 1
+name = nil
+
+opts.each do |opt, value|
+  case opt
+  when '--type':
+    unless ['USK','SSK'].include? value
+      puts "Type \"#{value}\" isn't recognised, please use USK or SSK"
+      exit 0
+    end
+    type = value
+  when '--name'
+    if value =~ /[^a-zA-Z0-9_-]/
+      puts "Name should consist of A-Z, 0-9, _ and - only"
+      exit 0
+    end
+    name = value
+  when '--keys' # Don't check keys, I have no idea how to.
+    keys = value.split(';')
+  when '--version'
+    version = value.to_i
+  when '--help'
+    RDoc::usage
+    exit 0
+  end
+end
+
+if ARGV.length != 1
+  puts "Missing dir argument (try --help)"
+  exit 0
+end
+
+dir = File.expand_path(ARGV.shift)
+name ||= basename(dir)
+
+Freenet::Site.add_site(:name=>name, :keys=>keys, :version=>version, 
:type=>type, :dir=>dir)
\ No newline at end of file

Modified: trunk/apps/rubyFreenet/rsite/rsite.rb
===================================================================
--- trunk/apps/rubyFreenet/rsite/rsite.rb       2006-06-10 03:18:04 UTC (rev 
9121)
+++ trunk/apps/rubyFreenet/rsite/rsite.rb       2006-06-10 08:12:10 UTC (rev 
9122)
@@ -1,15 +1,57 @@
 $:.push('../')
 require 'freenet'
+require 'yaml'

 module Freenet
   class Site
-    attr_accessor :version, :client, :keys, :name
-    def self.load(file)
-      Marshal.load(IO.read(file))
+    STORE_PATH = File.expand_path("~/.rubyFreenetSites")
+    def self.add_site(site)
+      existing_sites = load_sites
+      site = site.to_hash if site.respond_to? :to_hash
+      existing_sites << site
+      save_sites(existing_sites)
     end

+    def self.save_sites(sites)
+      sites = sites.collect do |s|
+        if s.respond_to? :to_hash
+          s.to_hash 
+        else
+          s
+        end
+      end
+      File.open(STORE_PATH, 'w') do |f|
+        f.flock(File::LOCK_EX)
+        YAML.dump(sites, f)
+        f.flock(File::LOCK_UN)
+      end
+    end
+    
+    def self.load_sites
+      if File.exists? STORE_PATH
+        sites = []
+        File.open(STORE_PATH) do |f|
+          f.flock(File::LOCK_EX)
+          sites = YAML.load(f)
+          f.flock(File::LOCK_UN)
+        end
+        sites ||= []
+        sites.collect do |s|
+          site = Site.new(s[:type], s[:dir], s[:name])
+          site.keys = s[:keys] if s[:keys]
+          site.version = s[:version]
+          site.last_update = s[:last_update]
+          site
+        end
+      else
+        []
+      end
+    end
+    
+    attr_accessor :version, :client, :keys, :name, :last_update
+
     def initialize(type, path, name)
-      raise SiteError.new('Invalid type') unless 
['USK','SSK','CHK','KSK'].include? type
+      raise SiteError.new("Invalid type: #{type}") unless 
['USK','SSK','CHK','KSK'].include? type
       @path, @type, @name = path, type, name
       @version = ''
     end
@@ -18,6 +60,10 @@
       @client = Freenet::FCP::Client.new()
     end

+    def disconnect
+      @client.disconnect
+    end
+    
     def generate_key
       @keys = @client.generate_keypair
     end
@@ -36,10 +82,11 @@
       @uri ||= Freenet::URI.new(@keys[0])
       @uri.type = @type
       @uri.path = "/#{@name}"
-      @uri.version ||= 0
+      @uri.version ||= @version
       @uri.version += 1
-      puts "Insert key: #{@uri.uri}\nRequest key: #{@uri.uri}"
-      @client.putdir(@uri, path)
+      @last_update = File.mtime(path)
+      uri = @client.putdir(@uri, path)
+      uri
     end

     # Insert a single file. You probably want a CHK for this, use it to insert
@@ -73,11 +120,13 @@
       end
     end

-    def save(file)
-      client = @client
-      @client = nil
-      File.open(file, 'w') {|f| f.write(Marshal.dump(self))}
-      @client = client
+    def to_hash
+      {:type=>@type,
+       :name=>@name,
+       :version=>@version,
+       :dir=>@path,
+       :keys=>@keys,
+       :last_update=>@last_update}
     end
   end


Added: trunk/apps/rubyFreenet/rsite/updatesites
===================================================================
--- trunk/apps/rubyFreenet/rsite/updatesites    2006-06-10 03:18:04 UTC (rev 
9121)
+++ trunk/apps/rubyFreenet/rsite/updatesites    2006-06-10 08:12:10 UTC (rev 
9122)
@@ -0,0 +1,14 @@
+#!/usr/bin/ruby
+
+require 'rsite'
+require 'getoptlong'
+require 'rdoc/usage'
+
+sites = Freenet::Site.load_sites
+sites.each do |site|
+  site.connect
+  puts site.insert_site
+  site.disconnect
+end
+
+Freenet::Site.save_sites(sites)
\ No newline at end of file


Reply via email to