Skip to content

Commit

Permalink
Added GCD timer source based session store.
Browse files Browse the repository at this point in the history
git-svn-id: http://svn.macosforge.org/repository/ruby/ControlTower/trunk@4885 23306eb0-4c56-4727-a40e-e92c0eb68959
  • Loading branch information
jballanc committed Nov 5, 2010
1 parent 301b7e4 commit c26c88a
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 0 deletions.
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ GEM_SPEC = Gem::Specification.new do |spec|
lib/control_tower/rack_socket.rb
lib/control_tower/server.rb
lib/rack/handler/control_tower.rb
lib/rack/session/gcd.rb
bin/control_tower
ext/CTParser/http11_parser.h
ext/CTParser/http11_parser.c
Expand Down
110 changes: 110 additions & 0 deletions lib/rack/session/gcd.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#
# Copyright © 2010 Apple Inc. All rights reserved.
#
# IMPORTANT NOTE: This file is licensed only for use on Apple-branded computers
# and is subject to the terms and conditions of the Apple Software License Agreement
# accompanying the package this file is a part of. You may not port this file to
# another platform without Apple's written consent.
#

require 'rack/session/abstract/id'

module Rack
module Session
class GCDSession
attr_reader :session_id

def initialize(sid, store)
@session_id = sid
@session_store = store
@session_values = {}
@session_timer = nil
@session_expires_at = nil
@session_access_queue = Dispatch::Queue.new("session-#{sid}-access")

# Sessions are self expiring. This is the block that handles expiry. The first task is to cancel the timer so that it only
# fires once
@timer_block = lambda do |src|
@session_timer.cancel! unless @session_timer.nil?
time_remaining = @session_expires_at - Time.now
if time_remaining < 0
@session_store.delete(@session_id)
else
@session_timer = Dispatch::Source.timer(time_remaining, 500, 0.5, @session_access_queue, &@timer_block)
end
end
end

def set_timer(seconds)
# If this is the first time we're setting the timer, then we need to create the timer source as well
@session_timer ||= Dispatch::Source.timer(seconds, 500, 0.5, @session_access_queue, &@timer_block)
@session_access_queue.sync { @session_expires_at = Time.now + seconds }
end

def [](key)
@session_values[key]
end
alias :fetch :[]

def []=(key, value)
@session_access_queue.sync do
@session_values[key] = value
end
value
end
alias :store :[]=

def delete(key)
@session_access_queue.sync do
@session_values.delete(key)
end
end

def clear
@session_access_queue.sync do
@session_values.clear
end
end
end

class GCD < Abstract::ID
def initialize(app, options={})
super
@sessions = {}
end

# Use UUIDs for session keys and save time on uniqueness checks
def generate_sid
uuid = CFUUIDCreate(nil)
CFMakeCollectable(uuid)
uuid_string = CFUUIDCreateString(nil, uuid)
CFMakeCollectable(uuid_string)
uuid_string
end

def get_session(env, sid)
session = @sessions[sid] if sid
unless sid and session
env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
sid = generate_sid
session = GCDSession.new(sid, @sessions)
@sessions[sid] = session
end
return sid, session
end

def set_session(env, sid, session, options)
session = @sessions[sid]
if options[:renew] or options[:drop]
@sessions.delete(sid)
return false if options[:drop]
session_id = generate_sid
@sessions[session_id] = 0
end
session ||= GCDSession.new(generate_sid, @sessions)
session.set_timer(options[:expire_after]) unless options[:expire_after].nil?
session.session_id
end
end
end
end

0 comments on commit c26c88a

Please sign in to comment.