Skip to content
This repository has been archived by the owner on Jun 21, 2018. It is now read-only.

g-andrade/nlocks

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ad7f902 · May 1, 2017

History

38 Commits
May 1, 2017
May 1, 2017
May 1, 2017
May 1, 2017
May 1, 2017
Jun 11, 2016
Jun 10, 2016
May 1, 2017
Jun 9, 2016
May 1, 2017
May 1, 2017
May 1, 2017
May 1, 2017
May 1, 2017

Repository files navigation

nlocks

Copyright (c) 2016 Guilherme Andrade

Version: 1.1.2

Authors: Guilherme Andrade (nlocks(at)gandrade(dot)net).

nlocks: Native spinlocks for Erlang


An experiment on Erlang native spinlocks:

  • Able to guarantee exclusive access to shared resources
  • Not dependent on a single process (no flooded mailbox as a bottleneck)
  • Able to deal with brutally killed processes that still held unreleased locks (more below)
Lock = nlocks:new(),
Transaction = fun() ->
    io:format("hello from ~p at ~p~n", [self(), os:timestamp()]),
    timer:sleep(100)
end,

[spawn(fun () -> nlocks:transaction(Lock, Transaction) end) || _ <- lists:seq(1, 4)].
% hello from <0.66.0> at {1465,509871,454813}
% hello from <0.69.0> at {1465,509871,555028}
% hello from <0.67.0> at {1465,509871,656021}
% hello from <0.68.0> at {1465,509871,757031}

Hackish solution. Other than setting up some sort of monitor in the Erlang land, I found no practical way to deal with these other than making use of ownership objects, references to which should never leave the process under which they were created; the release therefore becomes dependent on the garbage collector calling their destructor, but this also makes it more convenient for regularly terminated processes that forget to clean up.

struct Ownership {
    ERL_NIF_TERM pid;
    Lock** lockResource;
};
  • They're created for lock acquisition in order to deal with the above problem
  • They should never leave their owning process
  • On successful acquisition, they link themselves to the lock and force it to live as long as they live
  • They guarantee only the lock owner can release the lock
  • No explicit destruction needed
struct Lock {
    std::atomic<Ownership*> ownership;
};
  • They keep an atomic pointer either set to null (free) or to the ownership object's address (locked)
  • Can be shared between local processes through messages, ETS tables, etc.
  • No explicit destruction needed
  • It uses C++ 11 compare_exchange_weak
  • For each attempt, a new NIF call is scheduled; this brings some overhead to the table but it's crucial to play nice with the VM
  • If the timeout is other than 'infinity', for each attempt it compares the deadline against the current steady_clock value; this feels excessive and might hurt performance.
% nlocks:info()
[{allocated_locks,2},
 {allocated_ownerships,6},
 {acquired_locks,1},
 {contention,5}, % amount of processes attempting lock acquisition
 {has_lockfree_counters,true},
 {has_lockfree_ownership,true}]

Modules

nlocks