freenode/lisp - IRC Chatlog
Search
11:03:56
pls153
COUNTER ACTION other breaking news: someone is already developing a new browser in Rust: https://github.com/twilco/kosmonaut
13:03:10
Josh_2
mrcom: https://github.com/K1D77A/metalock all slot access happens using a read-writer lock now
14:53:00
puchacz
hi, what version of write / print function is equivalent of (format nil "~a" object) please? object being any object
15:01:01
puchacz
I have it with format nil already, maybe the compiler does something sensible with it, but I prefer to make it less ugly
15:24:29
puchacz
jackdaniel: your function gives a compilation warning about unused argument. something is wrong with it :)
16:14:19
flip214
If a macro wants to store a (static) value per-callsite (same for all threads), is the best way to use use the value slot of a gensym? Is there a more memory-efficient way if I just want to remember a fixnum? (cache for max. size of w-o-t-s)
18:22:10
mrcom
Josh_2: So I see! Forwarning--I've never been able to write DIY locking code that worked the first time, even after I was sure I debugged everything.
18:22:52
mrcom
Josh_2: ...and I see a bug in BEGIN-WRITE. You're calling CONDITION-NOTIFY in the loop instead of CONDITION-WAIT.
18:24:23
mrcom
Josh_2: Also, in END-READ you should consider calling (ASSERT (PLUSP RA)) instead of (UNLESS (ZEROP RA) ...)
18:25:20
mrcom
It might seem more forgiving to not do anything if it's already zero, but that case should never happen.
18:27:34
mrcom
Bitter, bitter experience has taught me that it's better to detect threading errors as early as possible. If RA is ever zero at that point then something's gone wrong.
18:32:58
mrcom
Josh_2: Also, in WRITE-WITH-RW-LOCK the second call is to END-READ instead of END-WRITE.
18:38:20
mrcom
Josh_2: Oh, wait. I was thinking HANDLER-BIND. You don't to execute END-READ or END-WRITE twice, which UNWIND-PROTECT will do it everything is OK.
18:41:56
mrcom
Also, in READ-/WRITE-WITH-RW-LOCK, if the BEGIN-READ or BEGIN-WRITE themselves fail you're going to have a mess.
18:45:40
Josh_2
I added unwind protect because I initially had a bug where I was checking the wrong var (pretty typical) and it was stalling, if I C-c'd it the whole thing would break
18:46:12
mrcom
Suggestion: Something like (let ((locked nil)) (unwind-protect (unwind-protect (begin-write ,lock) (setf locked t)) (if locked (progn ,@body)) (if locked (end-read/-write))
18:47:18
mrcom
And when you added the unwind-protect it looked like everything was OK because you also had the (unless (zerop ra)).
18:47:59
mrcom
Yeah, like I was I've never gotten this stuff to work first try. Or second. Or third :)
18:51:56
mrcom
Should be (let ((locked nil)) (unwind-protect (progn (begin-write ,lock) (setf locked t)) (if locked (progn ,@body)) (if locked (end-read-/-write)))
18:53:42
mrcom
... And I messed it up again. There should be a PROG1 around the two (IF LOCKED ...) expressions, so that you return the value of the body.
18:58:00
mrcom
(LOCALLY () ...) is the same as (LET () ...), except that if the LOCALLY is a top-level form so are the sub-forms.
18:58:44
phoe
PROGN is for when your body forms do not accept declarations; LOCALLY, for when they do
19:02:09
mrcom
Sigh. Still not right. The first UNWIND-PROTECT will return the value of the SETF, or an error if BEGIN-READ/-WRITE failed.
19:07:54
Josh_2
can't I only have 1 unwind protect? that will catch a C-c and then there will be only 1 unlock
19:10:48
Bike
(unwind-protect (progn (begin-write ,lock) (locally ,@body)) (end-write ,lock)) seems fine to me
19:13:17
Bike
but if you fail at grabbing the lock you probably shouldn't actually execute the critical section
19:13:26
flip214
Josh_2: then make the inner (if ,locked (end-write ...)) a (when ,locked (end-write ) (setf locked nil))
19:14:05
phoe
(let ((lock-grabbed-p nil)) (unwind-protect (progn (setf lock-grabbed-p (try-grab-lock)) (when lock-grabbed-p ...)) (when lock-grabbed-p (release-lock))))
19:18:36
flip214
not sure if end-write would return a useful value... can that fail as well? If yes, then my code would try to unlock a second time
19:19:19
flip214
mrcom: the primitives are hard - by definition. as soon as there's a high-level interface, there's not so much that can go wrong
19:20:12
mrcom
It's not quite up there with writing your own crypto, but it's definitely "just don't" if at all possible.
19:22:04
Alfr__
It's still bogus: What happens when it's interrupted after (begin-write ,lock) but before (setf ,locked t)?... Look at the (when ...) at the bottom.
19:32:25
Alfr__
mrcom, but that begin-write better be uninterruptible otherwise (end-write ..) may be called without owning the lock.
19:33:37
Bike
if begin-write is aborted there's no unlock. if the body is aborted there's an unlock. if the body returns normally there's an unllock.
19:35:47
Alfr__
Bike, is there a guarantee that should begin-write return, the unwind-protect is in effect?
19:38:35
markasoftware
I am writing a macro that takes a place as an argument. The place will be expanded multiple times. How can I avoid issues if the place passed in has side effects, for example, `(nth (incf i) foo)`?
19:38:41
Alfr__
Bike, the that uninterruptible part war for that earlier version of yours, where you had (unwind-protect (progn ...; no the other way around.
19:39:11
Bike
the way unwind-protect is usually written there's no actual time for an interrupt, and anyway, if you're worried about that there's no possible way to fix the problem since there's always some space after grabbing the lock
19:40:11
phoe
markasoftware: see https://github.com/phoe/phoe-toolbox/blob/master/phoe-toolbox.lisp#L193-L253
19:42:14
Alfr__
I was pondering whether a way out would be for each thread to cas it's unique id (> 0) onto some place in lock. Then whether one holds a lock could be determined later on.
19:43:04
mrcom
If we only had to worry about a discrete return value, as C or C++ do, then you can just take the value and return it afterward.
19:43:50
mrcom
However, Lisp is a lot richer environment. We've got to worry about conditions and closures.
19:45:54
mrcom
What I mean is we can't (unwind-protect (lock) (setf save-val (progn @,body)) (unlock) (return save-val))
19:49:25
Bike
the real right thing to do here is probably to use the implementation's underlying with-lock-held, if your read write locks are implemented on top of the implementation locks
19:53:17
phoe
my naïve approach for that would be (defmethod c2mop:slot-value-using-class (class object (slotd locked-slotd)) (bt:with-lock-held ((lock slotd)) (call-next-method)))
19:53:21
Bike
with the rwlock implementation i know you're not holding an actual lock during the write-locked section
19:55:09
Bike
but you can though? to begin-write you grab the internal lock, increment a variable, do some condition waiting, decrement the variable, set another variable, then unlock the internal lock
19:55:35
Bike
and if you're using the reader-preferring implementation all you need to do to grab the write lock is grab an actual lock, which you can use the system with-lock-held for
19:56:16
Bike
running the end-write procedure partway through the begin-write procedure seems fine to me
20:00:05
Josh_2
hmm so I can (unwind-protect the body of begin-write and have end-write as the cleanup?
20:12:01
mrcom
The fundemental problem is that Lisp doesn't really have a concept of critical sections.
20:15:36
phoe
https://stackoverflow.com/questions/9950680/unix-signal-handling-in-common-lisp has some pointers
20:32:40
exodus
Howdy, folks. Trying out curry/compose reader macros from here https://github.com/eschulte/curry-compose-reader-macros
20:33:07
exodus
I eval the four lines as the instructions says, but the rcurry errors out with _ is unbound
20:36:32
Xach
exodus: you must also use-package curry-compose-reader-macros or import _ explicitly from that package
20:39:32
markasoftware
as in, why did you not use it instead of the more complicated macro involving get-setf-expander?
20:43:04
Bike
(defun mod-add (n divisor delta) (mod (+ n delta) divisor)) (define-modify-macro mod-incf (divisor &optional delta) mod-add) would be fine, i think?
20:45:10
markasoftware
as for why SICL wouldn't use define-modify-macro for incf, who knows? looks like sbcl doesn't either. Maybe performance?
20:45:40
markasoftware
actually, the SBCL source code says "DEFINE-MODIFY-MACRO could be used, but this expands more compactly."
20:51:32
markasoftware
very interesting, all the setf stuff...never really understood it before today.
20:53:38
Bike
sicl's implementation only seems to bind one of the variables of the setf expansion, not sure what's goin on there
21:02:00
mrcom
Josh_2: The key things is that incrementing writer-wait, calling the body, and decrementing are all one atomic operation, with a window while it's spinning waiting for readers to clear.
21:15:16
phoe
I don't know if I can review that since it's late and I haven't had much experience with that part of the Lisp ecosystem
21:20:15
mrcom
I don't think so. It's really no more complicated than what you had; the BEGIN- and END- functions are just inlined.
21:21:36
mrcom
And it doesn't need WITHOUT-INTERRUPTS, so will work with Lisps that lack the equivalent (ABCL? ECL?)
21:25:32
Josh_2
in the example you sent the cleanup form of the first unwind-protect is the other unwind-protect
21:26:58
mrcom
It's just a way of saying that all four things always have to execute, even if there's an abort somewhere.
21:28:15
mrcom
(There could be another U-P around the DECF and the CONDITION-NOTIFY, but if the DECF doesn't work there's no point in notifying.)
21:33:35
mrcom
The active writer can't release the RW lock until it grabs the G lock. If there are a lot of readers this could take a while.
21:34:09
mrcom
Try following the same idea. Everything that's critical for the reader is under the one lock.
21:34:14
Josh_2
I don't need to use (multiple-value-list ..) in this instance as write-with-rw-lock isn't exported from the package
21:36:48
mrcom
Anyway, for the reader, instead of (with-lock (increment)) (read) (with-lock (decrement)), you want (with-lock (increment) (read) (decrement))
21:37:25
Josh_2
so basically just skipping the second lock grab and instead just performing the read in the middle
21:38:07
mrcom
Yep. In both cases, the only reason even to do the incr/decr is for the benefit of the while-loop "window".
21:44:37
mrcom
Not in your case. All you're doing is a slot reference. That's so fast that it's utterly swamped by the lock/unlock.
21:45:44
mrcom
Yeah, in this case you'd probably actually get better performance without the condition variables.
21:47:04
mrcom
Looks good, except you really don't want the "(unless (zerop ra) (decf ra))". You *always* want to decrement.
21:47:34
mrcom
You no longer have the double-decrement problem, which is what you were fixing with the "unless ...".
21:49:34
mrcom
You can tweak the code to get true simultaneous-readers, but remember that you're locking one slot on one instance. How frequently are you going to have two threads looking at the same thing at the same moment?
21:50:05
mrcom
The tweak would actually take more time than just waiting for the first reader to unlock.