Skip to content

Commit

Permalink
chore: use rwlock
Browse files Browse the repository at this point in the history
  • Loading branch information
elbywan committed Jan 21, 2024
1 parent 76b6c31 commit c4ce0d8
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 20 deletions.
2 changes: 1 addition & 1 deletion src/commands/install/protocol/resolver.cr
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ abstract struct Zap::Commands::Install::Protocol::Resolver
else
packages_ref = "#{name}@#{pinned_dependency}"
end
state.lockfile.packages_lock.synchronize do
state.lockfile.packages_lock.read do
state.lockfile.packages[packages_ref]?
end
end
Expand Down
4 changes: 2 additions & 2 deletions src/commands/install/resolver.cr
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ module Zap::Commands::Install::Resolver
lockfile_cached = uninitialized Bool
metadata = keyed_lock(metadata_key) do
# If another fiber has already resolved the package, use the cached metadata
lockfile_metadata = state.lockfile.packages_lock.synchronize do
lockfile_metadata = state.lockfile.packages_lock.read do
state.lockfile.packages[metadata_key]?
end
lockfile_cached = !!lockfile_metadata
Expand Down Expand Up @@ -181,7 +181,7 @@ module Zap::Commands::Install::Resolver
# Remove dev dependencies
_metadata.dev_dependencies = nil
# Store the package data in the lockfile
state.lockfile.packages_lock.synchronize do
state.lockfile.packages_lock.write do
state.lockfile.packages[metadata_key] = _metadata
end
end
Expand Down
3 changes: 2 additions & 1 deletion src/lockfile.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ require "yaml"
require "msgpack"
require "digest"
require "./utils/macros"
require "./utils/concurrent/rwlock"

alias DependencyType = ::Zap::Package::DependencyType

Expand Down Expand Up @@ -47,7 +48,7 @@ class Zap::Lockfile
@roots_lock = Mutex.new
@[YAML::Field(ignore: true)]
@[MessagePack::Field(ignore: true)]
getter packages_lock = Mutex.new
getter packages_lock = Utils::Concurrent::RWLock.new
@[YAML::Field(ignore: true)]
@[MessagePack::Field(ignore: true)]
property read_status : ReadStatus = ReadStatus::NotFound
Expand Down
50 changes: 50 additions & 0 deletions src/utils/concurrent/rwlock.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
class Zap::Utils::Concurrent::RWLock
@writer = Atomic(Int32).new(0)
@readers = Atomic(Int32).new(0)

def read_lock
loop do
while @writer.get != 0
Intrinsics.pause
end

@readers.add(1)

break if @writer.get == 0

@readers.sub(1)
end
end

def read_unlock
@readers.sub(1)
end

def read
read_lock
yield
ensure
read_unlock
end

def write_lock
while @writer.swap(1) != 0
Intrinsics.pause
end

while @readers.get != 0
Intrinsics.pause
end
end

def write_unlock
@writer.set(0)
end

def write
write_lock
yield
ensure
write_unlock
end
end
46 changes: 40 additions & 6 deletions src/utils/data_structures/safe_array.cr
Original file line number Diff line number Diff line change
@@ -1,20 +1,54 @@
{% if flag?(:preview_mt) %}
require "../concurrent/rwlock"

struct SafeArray(T)
property inner : Array(T)
@lock = Mutex.new
@lock = Zap::Utils::Concurrent::RWLock.new

def initialize(*args, **kwargs)
@inner = Array(T).new(*args, **kwargs)
end

def synchronize
@lock.synchronize do
yield @inner
{% begin %}
{% write_methods = [
:[]=,
:<<,
:clear,
:concat,
:compact!,
:delete,
:delete_at,
:fill,
:insert,
:pop,
:pop?,
:push,
:reject!,
:replace,
:rotate!,
:select!,
:shift,
:shift?,
:sort!,
:sort_by!,
:truncate,
:uniq!,
:unshift,
:unstable_sort!,
:unstable_sort_by!,
] %}

{% for write_method in write_methods %}
def {{write_method.id}}(*args, **kwargs)
@lock.write do
@inner.{{write_method.id}}(*args, **kwargs)
end
end
end
{% end %}
{% end %}

macro method_missing(call)
@lock.synchronize do
@lock.read do
@inner.\{{call}}
end
end
Expand Down
37 changes: 29 additions & 8 deletions src/utils/data_structures/safe_hash.cr
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{% if flag?(:preview_mt) %}
require "msgpack"
require "../concurrent/rwlock"

struct SafeHash(K, V)
getter inner : Hash(K, V)
getter lock = Mutex.new(:reentrant)
getter lock = Zap::Utils::Concurrent::RWLock.new

def initialize(*args, **kwargs)
@inner = Hash(K, V).new(*args, **kwargs)
Expand All @@ -13,18 +14,38 @@
@inner = Hash(K, V).new(*args, **kwargs, &block)
end

def synchronize
@lock.synchronize do
yield @inner
end
end

def to_msgpack(packer : MessagePack::Packer)
@inner.to_msgpack(packer)
end

{% begin %}
{% write_methods = [
:[]=,
:clear,
:compact!,
:delete,
:merge!,
:put,
:put_if_absent,
:reject!,
:select!,
:shift,
:shift?,
:transform_values!,
:update,
] %}

{% for write_method in write_methods %}
def {{write_method.id}}(*args, **kwargs)
@lock.write do
@inner.{{write_method.id}}(*args, **kwargs)
end
end
{% end %}
{% end %}

macro method_missing(call)
@lock.synchronize do
@lock.read do
@inner.\{{call}}
end
end
Expand Down
25 changes: 23 additions & 2 deletions src/utils/data_structures/safe_set.cr
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
{% if flag?(:preview_mt) %}
struct SafeSet(T)
property inner : Set(T)
getter lock = Mutex.new(:reentrant)
getter lock = Zap::Utils::Concurrent::RWLock.new

def initialize(*args, **kwargs)
@inner = Set(T).new(*args, **kwargs)
end

{% begin %}
{% write_methods = [
:<<,
:add,
:add?,
:clear,
:concat,
:delete,
:rehash,
:substract,
] %}

{% for write_method in write_methods %}
def {{write_method.id}}(*args, **kwargs)
@lock.write do
@inner.{{write_method.id}}(*args, **kwargs)
end
end
{% end %}
{% end %}

macro method_missing(call)
@lock.synchronize do
@lock.read do
@inner.\{{call}}
end
end
Expand Down

0 comments on commit c4ce0d8

Please sign in to comment.