http://qs321.pair.com?node_id=11135580


in reply to Is this a way to Go Perl #1

Frankly, I'm surprised that this much works this well.

As for the synchronization issue, the canonical way to solve it in Go is with a sync.WaitGroup, like this:

package main import ( "C" "fmt" "os" "sync" ) func main() {} var wg sync.WaitGroup //export Yaph func Yaph() { fmt.Println("Every day I get in the queue") fmt.Println("To get on the bus that takes me to you") } //export Acme func Acme() { wg.Add(1) go func() { defer wg.Done() fmt.Println("I guess i'm a closure") fmt.Println (os.Getpid()) }() } //export Wait func Wait() { wg.Wait() fmt.Println("All async goroutines have terminated") }

Then, in the calling perl code:

#!/usr/bin/env perl use strict; use warnings; use FFI::Platypus; use feature qw ( say ); say qq($0 $$); my $ffi = FFI::Platypus->new( api => 1 ); $ffi->lib('./yaph.so'); $ffi->attach(Yaph => []); $ffi->attach(Acme => []); $ffi->attach(Wait => []); Yaph(); Acme() for 1..300; Wait(); END { say qq(Too much, Magic Bus!) for 1 .. 3; } __END__

This has the disadvantage of relying on a global variable on the Go side. I don't see any way of returning a waitgroup variable created on the fly from an exported Go function to be encapsulated into a Perl variable, then feed it back into a second Go function. Nor do I see any other way to influence the Go runtime from the Perl side.

Still, it's an impressive demonstration. Real multithreading in a Perl program! :P

Replies are listed 'Best First'.
Re^2: Is this a way to Go Perl #1
by karlgoethebier (Abbot) on Aug 03, 2021 at 15:56 UTC
    «… Still, it's an impressive demonstration. Real multithreading in a Perl program!»

    Sure. This was the basic idea. And I wonder also why it works. A lot of stuff for further exploration. Best regards, Karl

    «The Crux of the Biscuit is the Apostrophe»

Re^2: Is this a way to Go Perl #1
by karlgoethebier (Abbot) on Aug 03, 2021 at 17:07 UTC
    «…relying on a global variable on the Go side…»

    Sure. But everything has it’s price. What if we use MCE and MCE::Shared? The shared variable where we write the output to is a global - less or more and however we set it’s scope. And regardless if we fork or spawn some threads: We rely on a global. If I’m not totally wrong 😑

    «The Crux of the Biscuit is the Apostrophe»

Re^2: Is this a way to Go Perl #1
by karlgoethebier (Abbot) on Aug 06, 2021 at 05:11 UTC
    «…the canonical way…»

    I’m not so sure about this.

    Please see here for a discussion about this issue.

    It’s answer #4.

    And BTW: In the example ibidem there is this  words := []string{"foo", "bar", "baz"} construct. Do you have any idea how to pass this as a param from Perl to the exported function?

    Best regards, Karl

    «The Crux of the Biscuit is the Apostrophe»

      I’m not so sure about this.

      And I'm quite sure about it :) -- that for this specific use case, where you start a bunch of goroutines, then wait until all of them are finished, sync.WaitGroup is the canonical way. To quote from the accepted answer of your StackOverflow link, "What is idiomatic in Go is to use the simplest and easiest to understand solution: here, the WaitGroup convey both the meaning (your main function is Waiting for workers to be done) and the mechanic (the workers notify when they are Done)."

      Other use cases may necessitate other solutions, but even then, inventing your own clever way of synchronizing between goroutines with a spaghetti of channels is not advisable. A few standard, idiomatic patterns have emerged by now, it's better to stick to those.

      By the way, I've thought a bit more about the potential uses of this go-perl hybrid, and I had to come to the sad conclusion that there aren't many. Unfortunately the interface between Perl and Go code has to go through two different interfaces (FFI and cgo), each of which brings its own set of compromises and limitations.

      For example, even though Go supports returning multiple values from functions, as does Perl, you can't use this, because the interface between the two uses C functions, which are limited to single returns. Even those are further constrained: you can't return Go pointers from a Go function, nor anything that contains a Go pointer. You have to use clumsy workarounds like go-pointer to get around this limitation. Passing any structured or array data into Go from C/Perl is similarly hard. (See also https://eli.thegreenplace.net/2019/passing-callbacks-and-pointers-to-cgo/)

      Exploiting Go's concurrency support is also a non-starter: even though it is possible to embed a Perl interpreter in Go (see Campher), and thus, in theory, to pass Perl coderefs to Go code and execute them there, you must take care to never use the Perl interpreter concurrently from different goroutines. So you'd have to run separate Perl interpreters in each goroutine you'd use for evaluating perl code, which is just like Perl's native ithreads model, except slower and even less robust.

      And BTW: In the example ibidem there is this words := []string{"foo", "bar", "baz"} construct. Do you have any idea how to pass this as a param from Perl to the exported function?

      Unfortunately, no. I've found seemingly relevant explanations (#1, #2), but I haven't tried to make it work myself.