leafac
2017-10-5 13:42:27

Is there an easy way to background a shelled out process in Racket? Currently, I’m using system, but adding a & to the end of the command does not behave the same as in Bash, Racket still blocks, waiting for completion.


pnwamk
2017-10-5 13:44:43

subprocess?


leafac
2017-10-5 13:47:47

Hmmm, I was hoping you wouldn’t say that :stuck_out_tongue: I’m trying to avoid having to close ports and so forth.


pnwamk
2017-10-5 13:48:22

there may be a better option — I’ve just used subprocess before and it worked well


pnwamk
2017-10-5 13:50:45

(I may have used process — can’t remember…)


leafac
2017-10-5 13:51:37

My use case is I have a bunch of commands to run in a loop, and they’re all independent. Moreover, I don’t care about the outputs, I’m running them for their side-effects (creating files on the disk). I wrote a for loop which calls system, and it worked fine, but it was a waste of time waiting the things to go serially. Then I wrapped the thing in (thread (λ () ___)). It works, but I was looking for a more principled solution.


leafac
2017-10-5 13:52:20

Of course, subprocess would work just as well, but then I’d have to juggle the many ports I don’t care about.


leafac
2017-10-5 13:52:52

Ideally, there’d be a for/parallel, which uses a thread pool and all.


leafac
2017-10-5 13:53:19

But I couldn’t find such a thing in the stdlib.


pnwamk
2017-10-5 13:53:54

would a for/thread be a sensible for-form?


pnwamk
2017-10-5 13:54:55

it would have to dome something (maybe throw away…) the thread descriptors… that seems… not ideal


leafac
2017-10-5 13:58:33

I’m running this in DrRacket, so even throwing away the thread descriptors works, because the main thread sticks around anyway. But, to make for/thread (or for/parallel, as I called it) also work on the command-line, it’d map thread-wait on thread descriptors. The whole for form would block. While I’m here wishing things would exist, for/parallel would have a thread pool to avoid starvation.


pnwamk
2017-10-5 14:01:35

would for/thread want to use exit-handler to update what should happen on exit, then? (I have no idea if that’s a sensible suggestion, BTW — we’re way beyond my area of experience at this point, lol)


leafac
2017-10-5 14:03:58

I guess the whole for/thread form could avoid blocking if it used exit-handler. But I wasn’t even this ambitious. I’d be happy if the for form itself blocked, but iterations ran in parallel.


leafac
2017-10-5 14:07:25

In fact, I’d say blocking the whole form would be even more useful. Because then I’d be easy to construct parallel pipelines with the necessary contention points. For example, if I have a directory full of PDFs I want to process,¹ I can sequence a bunch of for/parallel and it’d never try to process a file before it’s ready.

¹ Oh, look at that! A directory full of PDFs is exactly what I have here :slightly_smiling_face:


notjack
2017-10-5 17:45:19

@leafac a thread pool probably wouldn’t do what you think it does most of the time


lexi.lambda
2017-10-5 17:48:38

If you want, you can create a new custodian and use it when creating your processes, then call custodian-shutdown-all to close any dangling ports all at once.


lexi.lambda
2017-10-5 17:49:16

But, as @notjack says, I don’t think a thread pool makes sense in Racket (since Racket threads are green threads that all run on the same OS thread).


notjack
2017-10-5 17:53:29

Also the Racket process isn’t the one doing the work in this case. Racket has no idea how many OS threads or how much memory the bash subprocesses are using and logically it shouldn’t, so there isn’t really a sensible way for Racket to properly manage the resources consumed by those subprocesses.


notjack
2017-10-5 17:54:08

Racket’s the client, not the server. Client load control is usually done via throttling / rate limiting.


notjack
2017-10-5 17:54:48

so instead of “run these tasks on X threads”, think of it as “make no more than X requests per second”


lexi.lambda
2017-10-5 17:57:24

Right… which is why, if you truly want to run a bunch of subprocesses in parallel, Racket’s threads are good enough. They’ll create a bunch of subprocesses concurrently, and the subprocesses will actually run in parallel (being, well, separate processes).


lexi.lambda
2017-10-5 17:59:05

You could just do (apply sync (for/list ([i (in-range 10)]) (thread (lambda () (system "blah"))))) to run a bunch of blah commands in parallel and wait for them all to finish.


notjack
2017-10-5 17:59:29

a throttling package would be a useful thing to have


lexi.lambda
2017-10-5 17:59:45

Err… no, (apply sync ....) isn’t right, since that would wait for any one to finish. You would want (for-each sync ....).


lexi.lambda
2017-10-5 18:00:24

You could implement throttling pretty trivially with a single semaphore.


notjack
2017-10-5 18:00:25

hey lexi didn’t you write a for/async macro and stick it in some package?


lexi.lambda
2017-10-5 18:00:31

no


notjack
2017-10-5 18:00:47

must be thinking of something else then


leafac
2017-10-5 18:01:24

I’m probably misunderstanding something about thread pools. My idea was that if I had to run 1000 processes, I might not want to run them all simultaneously, but only have, say, 100 active at a time. How would this go wrong?

How is (for-each sync ___) different than (map thread-wait ___)?


lexi.lambda
2017-10-5 18:01:55

@leafac It isn’t really, except that for-each discards the results.


notjack
2017-10-5 18:02:04

@leafac because each process could spawn any number of its own threads


notjack
2017-10-5 18:02:18

you don’t want to only have 100 processes active at one time, you want 100 OS threads active at one time


notjack
2017-10-5 18:02:46

so limiting the processes may indirectly work, or it might not


lexi.lambda
2017-10-5 18:02:55

If you wanted to throttle to n things running at a time, you could just create a semaphore with (make-semaphore n) and wrap each system call with call-with-semaphore.


lexi.lambda
2017-10-5 18:06:04
(let ([s (make-semaphore 5)])
  (for-each thread-wait
            (for/list ([i (in-range 10)])
              (thread (thunk (call-with-semaphore
                              s (thunk (system (~a "sleep 1 && echo " i)))))))))

leafac
2017-10-5 18:06:48

Oh, right. I had (void (map ___)). That was naïve :stuck_out_tongue:

In any case, sync is equivalent to thread-wait in my use case, as I understand from reading the documentation. Is this right?


notjack
2017-10-5 18:06:55

yes, that’s right


lexi.lambda
2017-10-5 18:07:03

Yes, IIRC sync and thread-wait do the same thing on threads.


leafac
2017-10-5 18:07:08

:+1: Thanks.


leafac
2017-10-5 18:08:28

I’m learning so much from this conversation. I just read about thunk, it’s great!


leafac
2017-10-5 18:10:01

Alexis, you nailed it! That’s the implementation for the feature I wanted to find in the stdlib.


lexi.lambda
2017-10-5 18:10:45

Personally, I think it’s small enough and easy enough to build out of existing, composable pieces that there doesn’t need to be a separate abstraction for it. But YMMV.


leafac
2017-10-5 18:10:49

I understand @notjack’s point about OS threads, but, in my use case, it doesn’t matter.


notjack
2017-10-5 18:11:00

in the stdlib I’d want something where doing each for loop iteration concurrently and throttling are separate concerns


lexi.lambda
2017-10-5 18:11:33

Isn’t that what the semaphore and the use of thread are? Separate concerns?


notjack
2017-10-5 18:11:43

ugh for/async in racket/future is not well named :/


notjack
2017-10-5 18:11:47

no I mean


notjack
2017-10-5 18:12:01

there’s more ways to throttle than just “x concurrent tasks”


leafac
2017-10-5 18:12:04

How about a for/parallel, with optional #:pool-size argument?


notjack
2017-10-5 18:12:47

for/thread / for/future / for/place would be the names I’d want


lexi.lambda
2017-10-5 18:13:07

@leafac The thing here is that you aren’t making a pool of workers and distributing work over them. That isn’t really what you want from Racket’s threading model. Threads are cheap; feel free to make a million of them. Rather, you want to limit the number of processes you actually spawn at a time.


lexi.lambda
2017-10-5 18:13:35

A “thread pool” generally means you have a pool of workers, which isn’t the case here.


notjack
2017-10-5 18:13:47

yes, I wouldn’t want any for forms to have any knowledge of pooling tasks or other kinds of resource control


notjack
2017-10-5 18:13:53

there’s too many different ways to do it


lexi.lambda
2017-10-5 18:14:20

@notjack, you’re great, but I remain unconvinced that you do not overengineer these things. :)


notjack
2017-10-5 18:14:40

@lexi.lambda maybe I’m just planning for a different future :p


notjack
2017-10-5 18:15:29

I’m very much thinking with web servers in mind right now


pnwamk
2017-10-5 18:15:41

from left field: would it be for/threads (i.e. plural)?


pnwamk
2017-10-5 18:16:00

XD


notjack
2017-10-5 18:16:07

¯_(ツ)_/¯


leafac
2017-10-5 18:16:15

My intent is: (1) given (for ___ (system ___)), I can make this run faster just by replacing for with for/<your-favorite-name-here>; and (2) this doesn’t freeze my DrRacket because it tried to spawn a thousand subprocesses. If threads are cheap, then something else is making DrRacket hang. Do you know what it could be?


lexi.lambda
2017-10-5 18:16:38

I guess a for/thread(s) could be useful, but it really would just be a tiny abbreviation for wrapping the body of a use of for/list in (thread (thunk ....))


notjack
2017-10-5 18:17:06

arguably, there’s a lot of tiny abbreviations in the for/* forms


lexi.lambda
2017-10-5 18:17:28

Everything is just for/fold at the end of the day. :)


lexi.lambda
2017-10-5 18:17:41

(…which in turn is just named let, which is just recursion…)


notjack
2017-10-5 18:17:44

a fold a day keeps the object oriented programmers away


ben
2017-10-5 18:17:47

please don’t call if for/threads … call it ~parallel-for~ (edit: maybe thread-comprehension ?) at least, or make a map-reduce package


leafac
2017-10-5 18:17:58

Moreover, consider the case (1) above. I don’t want to think about threads or thunks, I just want my typesetting tasks to go faster :slightly_smiling_face:


notjack
2017-10-5 18:17:58

it’s not parallel though


lexi.lambda
2017-10-5 18:18:01

@ben but it isn’t parallel


ben
2017-10-5 18:18:16

async-for?


pnwamk
2017-10-5 18:18:20

what’s wrong with for/threads


pnwamk
2017-10-5 18:18:29

out of curiosity


notjack
2017-10-5 18:18:36

@leafac you have to think about threads and the difference between IO and CPU tasks to do this without getting weird unexpected results though


ben
2017-10-5 18:19:36

because for/X means X is the accumulator; with for/threads the threads aren’t really the accumulator


leafac
2017-10-5 18:19:40

Hmm, this is similar in spirit to & in Bash. And I don’t think about IO or CPU when using that.


lexi.lambda
2017-10-5 18:19:56

@leafac I think Racket’s threading model is relatively simple compared to most languages, and I think it’s worth learning. So much of the benefits of FP are building big things out of small, composable things. I think big abstractions (looking at you, LOOP) are usually only desirable when either incredibly common or too hard to build out of smaller pieces.


lexi.lambda
2017-10-5 18:20:11

& in bash is basically thread in Racket.


notjack
2017-10-5 18:20:17

@leafac in Bash nearly all tasks ever are IO tasks, so bash can provide a simpler model of parallelism without breaking too many unstated assumptions


notjack
2017-10-5 18:20:33

a general-purpose form for Racket would not have that luxury


pnwamk
2017-10-5 18:20:44

@ben that is not strictly the case (regarding accumulators)


lexi.lambda
2017-10-5 18:21:02

@ben I wish that were the case, too, but that’s already sort of been broken in the stdlib.


pnwamk
2017-10-5 18:21:06

there are lots in the standard lib that break that convention


notjack
2017-10-5 18:21:34

part of the problem is what racket calls threads are green threads, which are completely and entirely different from OS threads, making “thread” a highly overloaded term


lexi.lambda
2017-10-5 18:23:25

I really like Racket’s concurrency model. I wish the parallelism story were better, but I think the concurrency model is pretty easy to understand and work with, more so than most other languages I’ve used.


notjack
2017-10-5 18:24:00

yeah racket concurrency is really pleasant to work with, and I can generally get a feel for how I would do complex things with it


leafac
2017-10-5 18:24:57

Oh, I find my for → for/<make-this-faster> argument so good. It’s a shame no one else here is falling for it :stuck_out_tongue:

Of course, your concerns make sense when writing serious programs. But I’m writing a script to typeset my Redex models. This is not rocket science :stuck_out_tongue:


notjack
2017-10-5 18:25:35

true. I think a for/<make-this-faster> form would be really great for something like Rash or one of those other “racket-but-for-shell-script” hashlangs


notjack
2017-10-5 18:25:51

maybe even it should be the default in that sort of context


lexi.lambda
2017-10-5 18:25:55

@leafac I think I would agree with you if Racket threads ran in parallel, but they don’t. So using threads without understanding what they’re doing would likely be confusing, and making that too accessible might be misleading.


lexi.lambda
2017-10-5 18:26:52

Having a parallel-map sort of feature is a great one. But Racket can’t really have that (barring futures, which I am convinced are not currently especially useful outside of extraordinarily specific scenarios).


lexi.lambda
2017-10-5 18:27:05

Of course, Chez changes things.


lexi.lambda
2017-10-5 18:28:40

If you had a for/make-this-faster, you would get confused people writing (for/make-this-faster ([i (in-range 100)]) (some-cpu-bound-racket-computation!)) complaining on the mailing list.


notjack
2017-10-5 18:29:00

actually with real threads, that would be faster but IO bound tasks would be slower


leafac
2017-10-5 18:29:03

I don’t understand Racket’s parallel/concurrency models. I don’t even understand why Racket threads don’t run in parallel.¹ But I added the (for-each thread-wait (for/list (thread (thunk ___))))) thing and it did make things go faster.² That’s more then I could’ve asked :slightly_smiling_face:

¹ Yes, I’ll read the documentation and learn about this, now that I’m curious. ² At the cost of making DrRacket hang for approximately 10 seconds.


lexi.lambda
2017-10-5 18:29:14

@notjack That’s essentially my point.


notjack
2017-10-5 18:29:32

@lexi.lambda oops I thought you meant in the context of real threads run on chez or something


lexi.lambda
2017-10-5 18:29:58

No, I was talking a theoretical for/async-y thing using green threads.


notjack
2017-10-5 18:30:06

gotcha


leafac
2017-10-5 18:32:01

DrRacket should show me a progress bar when working through the for/make-this-faster :stuck_out_tongue:


lexi.lambda
2017-10-5 18:32:02

@leafac As for why Racket threads don’t run in parallel, I think a large part of it is that Racket was not designed to be made parallel, and making it parallel turned out to be unreasonably hard. :)


lexi.lambda
2017-10-5 18:32:37

There’s a talk mflatt gave a while back (at Mozilla, IIRC?) about some of the history around trying to make Racket parallel.


leafac
2017-10-5 18:32:44

It has some sort of Global Interpreter Lock (GIL)? I remember that from my days of Ruby…


notjack
2017-10-5 18:33:03

@leafac racket doesn’t have a GIL



lexi.lambda
2017-10-5 18:33:42

@notjack racket might as well have a GIL


leafac
2017-10-5 18:33:59

:+1: I’ll watch this later, thanks.


notjack
2017-10-5 18:34:11

@lexi.lambda how so?


lexi.lambda
2017-10-5 18:35:05

I’m being a little facetious, but I’m saying Racket’s current runtime is not really able to run in parallel, whether it has an actual GIL or not, so the result from the user’s POV is more or less identical


lexi.lambda
2017-10-5 18:35:46

what I’m really saying is that responding to people who ask “does Racket have a GIL?” with “no” is technically correct, the best kind of correct


lexi.lambda
2017-10-5 18:36:04

but what they’re really asking is “can I run my Racket in parallel?”


lexi.lambda
2017-10-5 18:36:20

and the answer is “not in the way you’re probably hoping for”


notjack
2017-10-5 18:36:43

I think no parallelism with IO in terms of green threads is a really different model than no parallelism with IO in terms of OS threads, and the latter is why a GIL is so painful - in the former a GIL isn’t nearly as big an issue and users won’t think of those two cases as the same thing


lexi.lambda
2017-10-5 18:37:28

you can have the latter with a GIL


notjack
2017-10-5 18:39:35

you have both with and without a GIL - what I mean is that it’s only the latter where a GIL is a huge problem for almost all users. In the former, a GIL is a problem much less often to the point where most users, even those doing complex things, wouldn’t need to care


notjack
2017-10-5 18:41:37

hmmm, I think I said that wrong


lexi.lambda
2017-10-5 18:41:41

I’m not sure I understand why the latter is made any more complicated by a GIL


notjack
2017-10-5 18:42:25

y’know I’m not sure what my point was anymore


lexi.lambda
2017-10-5 18:43:13

alright


notjack
2017-10-5 18:45:08

I think some a scribble book about general concurrency and parallelism concepts and where Racket’s model fits in would be a great thing to have in the docs


mtelesha
2017-10-5 19:24:42

(require ffi/winapi) (win64? ) What argument is (win64?) wanting? I would figure it was (= (system-type ’word) 64)


jaz
2017-10-5 19:53:47

leafac
2017-10-5 20:14:10

@lexi.lambda, @notjack: Just got a chance to get back to this. Thanks a lot for the conversation. I certainly learned a lot from you :slightly_smiling_face:


notjack
2017-10-5 20:14:34

:D


mtelesha
2017-10-5 21:36:29

@jaz I understand its not a procedure but I don’t understand (win64? )’s arguement it is looking for.


lexi.lambda
2017-10-5 21:40:23

@mtelesha what @jaz is saying is that win64? is not a function, so it does not accept any argument. it is a value. win64? is either #t or #f. I don’t think your question makes very much sense.


mtelesha
2017-10-5 21:42:20

@lexi.lambda Well I should say I expected (win64?) to return #t or #f. I am answer from the point of stupid. How are you suppose to use (win64?)


lexi.lambda
2017-10-5 21:42:36

don’t put any parentheses around it. just write win64?.


lexi.lambda
2017-10-5 21:43:02
> win64?
#f

mtelesha
2017-10-5 21:43:20

I was correct I was from the point of stupid :slightly_smiling_face:


apg
2017-10-5 23:07:51

hi folks… maybe especially @stamourv — what are the lunch options near the venue on Saturday?


zenspider
2017-10-5 23:09:29

@apg walk west to University Ave (just called “the ave” if you ask for directions) and take your pick… there is a ton. Probably stuff on campus too, but it won’t be half as good.


zenspider
2017-10-5 23:09:51

what do you like?


apg
2017-10-5 23:12:53

@zenspider ah. thanks! I wasn’t sure if there was a recommended campus spot, or if people would just self-organize and find something.


apg
2017-10-5 23:13:03

I’m vegan, so just trying to plan ahead.


zenspider
2017-10-5 23:14:18

there’s many options for you… go north on the ave for explicitly vegan options. Pizza∏ and others nearby…


ben
2017-10-5 23:14:23

there’s at least 2 other vegans attending


apg
2017-10-5 23:14:43

Pizzaπ isn’t as good as it used to be.


apg
2017-10-5 23:14:55

and is fairly far from the venue, no?


zenspider
2017-10-5 23:15:25

it’s a short walk


apg
2017-10-5 23:15:49

maps say 22 minutes from Mary Gates Hall.


apg
2017-10-5 23:16:05

(doable, sure)


zenspider
2017-10-5 23:16:07

there’s a korean place closer to our venue that I love… don’t know the name, but it’s about 42nd just off the ave


apg
2017-10-5 23:16:46

@zenspider @ben thanks for the info. :stuck_out_tongue:


zenspider
2017-10-5 23:17:34

Araya’s Place is also popular w/ my vegan friends


zenspider
2017-10-5 23:17:37

and closer


apg
2017-10-5 23:17:40

I guess if I play my cards right, I might even be able to go to Full Tilt for ice cream.


apg
2017-10-5 23:17:50

Araya’s is good food.


stamourv
2017-10-6 00:50:16

apg: Food will be provided, including vegan options.


apg
2017-10-6 02:50:05

Oh! Even better! Thanks, @stamourv !