
I am trying to write some code that will extract the contents of the provide
directive from a racket file. I’ve started down the path of just trying to convert the code into one giant sexpr. I know racket has stellar language parsers, but I am really just after one piece of information (I’ll be traversing a directory tree reading racket files for this info). I know I can just convert the file into a string and find what I am looking for in a regex-match, but was thinking the conversion of code into data would be a better approach. Any suggestions?

@trhawes You can expand the module. During expansion the expander records information on what is provided. See https://docs.racket-lang.org/reference/Expanding_Top-Level_Forms.html?q=provide#%28part._modinfo%29

Trying to figure out how to use Racket’s synchronization primitives, async channels and threads. If two threads sends data back and forth over a (unbounded, async) channel, how can I detect deadlock - thread 0 is blocking on read and thread 1 is blocking on read. Do I need to wrap the channel in a semaphore somehow?

It feels like there is an elegant solution here that’s eluding me.

@stefan.kruger I think the answer is usually “don’t do that.” Why do you need two threads to coordinate that way? If it’s because they share state, then usually you’d want to use some sort of lock-protected mutable state. If it’s because they need to send messages to one another, then usually you’d use async channels or thread mailboxes (which are themselves basically async channels).
If you want backpressure in that kind of system, you still probably want unidirectional data flow rather than having threads that are both reading and writing to the same channel. If there’s a 1-to–1 mapping from input messages to output messages, what about using two async channels, each with a buffer size of 1? One of them is a channel from thread A to thread B, and the other is a channel from B to A. That way, you can’t ever have deadlock as long as each thread doesn’t commit a protocol violation (which is a much bigger sin than just happening to both read at the same time).

Unless what you’re saying is not that you’re trying to figure out how to write a deadlock-free algorithm but how to detect deadlock for error-reporting purposes (where the “recovery” from the deadlock is just to panic).

What I’m trying to do is a (very) simple simulation of two processes where both can consume data from and produce data for the other — one of the Advent of Code exercises. It’s easily done without threads (as I did in my python solution: https://github.com/xpqz/aoc-17/blob/master/day18p2.py) but thought it might be instructive to try the asynch channel approach for learning.

So process A needs to know if to block, unless process B is already waiting for process A, in which case we;re done.

Or like Matthew Butterick’s https://github.com/mbutterick/aoc-racket/blob/master/2017/d18/main2.rkt

Could you tell me what the problem is? I don’t think I can actually access part 2 without solving part 1.


I made part 1 like so:


Yes, I mean I don’t think the advent of code website shows the problem statement for part 2 until you have solved part 1, and I have not solved part 1.

Ok, one second.


I see, so the machines deadlocking is fundamentally part of the problem. In that case, I don’t think Racket provides any built-in way to ask if two threads are both deadlocked on one another.

I do believe Racket actually does detect many kinds of deadlocks, since IIRC, a thread will be garbage collected if the runtime determines it is blocked on something that will never unblock. But that’s a best-effort sort of thing, so there aren’t any guarantees that the deadlock will be detected or that the thread will be GC’d in any reasonable timeframe.

Can I set a time-out on a an async channel read?

Yes, you can do that, but the trouble with that kind of approach is that it’s fundamentally a race condition.

Or otherwise back to some kind of asynch-channel-get-try + some global state?

Or a separate out-of-band channel that says other is blocked?

Clojure approach: https://gist.github.com/lnostdal/3409086d436359888ade175311fe0a55 — although I admittedly don’t understand that fully.

Yes, you could definitely do that. You could have some sort of intermediary thread that is actually managing the flow of values between the two other threads, and you could have a protocol where a read request involves sending a special “read” message to an outbound channel followed by reading from an inbound channel, while a send request involves sending some other message to the outbound channel. The manager thread could coordinate the two threads by doing some bookkeeping that tracks if both threads have sent more read requests than write requests and declaring them deadlocked if that happens. But I’m not sure that’s especially elegant.

Should have gone for the obvious solution, clearly :slightly_smiling_face:

@stefan.kruger Do you happen to know “The little book of semaphores”?


It contains solutions for common scenarios.

I am not sure, but section “4.3 No-starve mutex” sounds a little like your problem.

Wow that looks like a book I could have done with at university…

@soegaard2 The trouble here is that the problem isn’t about preventing deadlocks or starvation—the problem statement explicitly requires allowing deadlocks—the problem is detecting when a deadlock has occurred.

Sorry - skipped the actual problem statement. A manager thread seems a good idea.

It’s possible that there’s a solution without a manager thread using locks or events that I’m overlooking, but I haven’t been able to come up with one given the primitive Racket provides.

Is it enough for thread A and B to send messages “reading”, “done reading” “writing” “done writing” each time they use the channel connecting A and B? If both threads read at the same time, the manager knows there is a deadlock.

No—it’s especially tricky due to the fact that the processes are communicating via buffered channels, so detecting that both threads are reading simultaneously isn’t enough. They need to both be reading simultaneously and have nothing left to read. Otherwise there’s a race condition where both threads might be reading at the same time, but they are not deadlocked.

So I think what’s needed is (other-process-blocked? my-pid)
to be checked when (async-channel-get-try ...)
returns #f
.

I know that the other process when blocked cannot unblock unless I put a value on the channel, so when I’m in the read bit there should be no race.

It is not very clear from the linked docs how one might expand a racket file. I am going to load something close to 70 racket files in one pass, is this going to be the most efficient way?

Is this supposed to fail with megaparsack?: (parse-string (string-ci/p "LDA") "lda")
=>
(failure (message (srcloc 'string 1 0 1 2) #\d '("DA")))
I had a number of things failing and I just couldn’t figure out why. They all turned out to be due to this, as they work if the strings are of the same case. Am I misunderstanding the way string-ci/p (or string-ci=? in general) works? (string-ci=? "LDA" "lda")
works just fine.

I’m likely way off, but the code for string-ci/p calls chars/p: (define (string-ci/p str)
(chars/p str char-ci/p))
which looks like: (define (chars/p str char-parser)
(if (zero? (string-length str))
(pure "")
(label/p str (do (char-parser (string-ref str 0))
(string/p (substring str 1))
(pure str)))))
so, string-ci/p only seems to test the first character case-insensitively (using char-parser), but then defaults to string/p to recur on the rest of the string. I think?
Apologies if this is just a silly error on my part.

@sydney.lambda That looks like a bug in megaparsack. I suggest opening an issue https://github.com/lexi-lambda/megaparsack

Thanks @notjack. I always err on the side of “I’m doing something wrong” rather than “there’s a bug in the library”, so I thought I’d better ask first.