
@mflatt Hmm…so apparently when I run raco setup
, the callback I give to scheme_add_managed_close_on_exit
never gets called.

(When I run the program directly though it does.

)

I did update it to use malloc though: https://github.com/LeifAndersen/racket-video/blob/master/video/private/init-mlt.rkt

I want a macro that expands to something different at toplevel than when nested. So I use a (for-syntax (define slide-function (make-parameter #’toplevel-slide))) but it says that syntax is invalid.

(require (for-syntax syntax/parse))
and tweaks it until it hopefully works

@fahree: you probably want syntax-local-context
http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-context%29%29

thanks

unhappily, I don’t exactly want syntax, since I’m passing slide as a first-class function and defining wrappers around it and don’t want to syntaxify everything.

let me think… I want some parameter for the “current context”, and slide will have to side-effect that context

@fahree : just to be clear, is this what you want? #lang racket
(slide blah) ;; top-level version
(map slide '(1 2 3)) ;; not the top-level version(let ()
(slide blah)) ;; not the top-level version
(slide blah) ;; top-level version again

@fahree or do you want something like this? #lang racket
(define myslide (slide blah))
myslide ;; acts as the top-level version
(side 'wrapper myslide) ;; myslide acts as the non-top-level version, the wrapper is a top-level one though

leif: I imagine you know that the registration is commented out in the committed version, so that’s not it. But you’re registering a Racket closure as a callback with scheme_add_managed_close_on_exit
, and nothing retains that closure from the GC’s perspective. So probably you’re seeing a crash when the custodian is shut down and tries to invoke the callback (but the callback is GCed). One easy fix is to pass the closure as both the third and fourth argument to scheme_add_managed_close_on_exit
.

You probably also want to deregister the process global when the counter goes to 0, in case init is later – that is, not in a nested mode

Also, beware that you have race conditions all over; the reason scheme_register_process_global
returns a value, for example, is to implement a kind of compare-and-swap, so don’t ignore the result.

You need to make the counter increments and decrements atomic somehow… I don’t immediately see or remember something exported from Racket to do that, though

Ya, I eventually gave up and commented it out the close()
, the commit I originally sent didn’t have it commented out.

Also good point. Would it be safe to use Racket’s atomic mechanism here? http://docs.racket-lang.org/foreign/Atomic_Execution.html?q=automic#%28def._%28%28lib._ffi%2Funsafe%2Fatomic..rkt%29._call-as-atomic%29%29

Also good idea, I didn’t realize the GC would collect the closure there. Thanks for pointing that out to me.

@leif No on ffi/unsafe/atomic
; see the first paragraph of that section

@mflatt OH, okay. So I need something that is atomic with places, okay.

Also, you say to deregister the process global, but the docs seem to only have a register function.

Yes, I take that back; don’t deregister, but instead re-init if the counter is 0

Ah, okay, that makes sense.

Thanks.

Alright, I just need to find something automic….its currently looking like I’ll have to use FFI locks.


That’s completely a hack, since it relies on many implementation details of Racket, but it should work

The key is that box-cas!
is implemented with a compare-and-swap at the processor level

Yup. Also wow….I never thought of making something in GCed memory and memcopy-ing it.

Good - never do that :slightly_smiling_face:

Seems very dangerous, but I suppose it can work with this implementation

Right

This won’t work though for: (let ([x (scheme_register_process_global counter-key #f)])
(cond [x x]
[else

(Which is trying to see if a counter has been created before trying to allocate one.)

True, you may end up creating a counter that doesn’t get used

True. I guess I could allocate the counter, try to install it, and then if I already had one installed, free the new counter.

The install is then automic and so a race there is okay.

Well, freeing memory that has been cast to _racket
is dangerous, so I don’t have a good suggestion

True, but I don’t see where I would be free-ing memory cast to _racket

The counter is cast to _racket

Oh…derp, in the example you copied, okay.

Thinking…

There’s also a problem with discovering that the count is 0 and deciding to call init
, versus discovering that the counter is 1 and knowing that init
has been called; you really want some signaling mechanism instead

Okay, you say that scheme_register_process_global
operates automically, is that correct?

Yes

Probably it works to use the FFI to get mzrt_sema_create
, etc., as defined in “mzrt.h”

hmm…worth a shot, I’ll give that a try.

Those are not explicitly made public, but I’m pretty sure they’ll be accessible on all platforms – except on builds that don’t have places, in which case you don’t need them

Ha, fair.

Is there any documentation for them, otherwise I will just assume they’re like traditional semaphores.

Meh, looks like they’re just regular semaphores, I’ll give it a shot, thanks.

@mflatt So, it does look like I will have to use the semaphores as a mutex rather than just storing the counter in there.

Because there is no way to get the current counter except for wait. Is that correct?

Correct

@georges-duperon what I wanted is that a toplevel slides calls register-slide, but a nested slide calls section.

not sure what’s the best way to do it, though

especially since what’s “toplevel” or not seems pretty dynamic to me.

@fahree I see a few ways to do this: either you interpret top-level statically, as in “not syntactically nested inside another slide”, or you interpret it dynamically, as “not during the execution of another (slide …) form”.

let’s say dynamically

@fahree In the first case, I’d use syntax-parameterize
, in the second parameterize
.

@fahree A third option: consider the values which get implicitly printed when evaluating a module, e.g. #lang racket
'printed
(let ()
'not-printed
'not-printed
'printed)
'printed
This behaviour comes from #%module-begin
, which wraps every expression with code that prints its result. You could devise a custom #%module-begin
which instead of always printing, does (let ([v the-expression]) (if (is-a-section? v) (register-slide v) (print v)))

For the second option (based on parameterize
), here’s a skeleton: (define slide-top-level? (make-parameter #t))
(define (myslide-f title body)
(if (slide-top-level?)
(parameterize ([slide-top-level? #f])
(slide title (body)))
(section title (body))))
(define-syntax-rule (myslide title . body)
(myslide-f title (λ () . body)))
(myslide title
(myslide nested-section-1 "body1")
(myslide nested-section-2 "body1"))

@fahree beware that with the second option, if you do (define tmp-variable (slide …))
directly within the module, the slide will be considered top-level, so you would have to write instead (define (make-the-slide) (slide …))
. The third option is a bit more difficult, because it involves always producing sections, and turning a section back into a slide (i’m not sure if it’s possible/easy with slideshow, or if some of the information is hidden away and hard to get back).

Thanks. I used a parameter + a simple syntax-rule to delay evaluation.

(If anyone is curious about the result, it’s at https://github.com/fare/better-stories/blob/master/fare-lambdaconf2017.rkt )

@fahree Pas mal :slightly_smiling_face: