Arc Forumnew | comments | leaders | submit | eds's commentslogin
1 point by eds 6535 days ago | link | parent | on: fork() in Arc

Now I have two questions about threads:

Is there a way to wait for a thread to end, other than something like

  (let th (thread (sleep 1))
    (until (dead th))
    (pr 'done))
Also, is there a way to make a thread-local variable so threads don't all try to update the same global variable? For example

  (= pid 0)
  
  (def test ()
    (let parent? (fork)
      (= pid (+ (* pid 2) (if parent? 0 1)))))
  
  (thread
    (test)
    (test)
    (test)
    (prn pid))
prints 0, 1, 12, 25, 102, 205, 822, 1645, which much larger than the expected 1, 2, 3, 4, 5, 6, 7, 8 because all the threads update the same global variable

-----

2 points by almkglor 6535 days ago | link

I recently added thread-local variables to Anarki (last week or week before last, maybe). To use:

  (= foo (thread-local))
  (foo) ; read the variable
  (= (foo) 42) ; write the variable
Also, I've added semaphores to Anarki. If you want to wait on another thread:

  (let s (sema)
    ; thread you're waiting on
    (thread
      (sleep 1)
      (sema-post s))
    ; monitoring thread
    (sync s)
    (pr 'done))
As mentioned, Anarki only ^^

-----

2 points by eds 6535 days ago | link

Looking at 'sync documentation, it would seem that you can also just call 'sync on a thread to wait for it to end.

  (let th
    ; thread you're waiting on
    (thread
      (sleep 1))
    ; monitoring thread
    (sync th)
    (pr 'done))

-----

1 point by eds 6535 days ago | link

Thanks, that is exactly what I wanted. Although it might be nice to have a macro wrapper around 'thread-local for initializing variables

  (w/uniq void
    (mac w/thread-local (var (o val void))
      (if (is val void)
	  `(= ,var (thread-local))
	  `(do (= ,var (thread-local))
	       (= (,var) ,val)))))
then you could do

  (w/thread-local foo 0)
instead of the two step initialization

  (= foo (thread-local))
  (= (foo) 0)

-----

1 point by eds 6536 days ago | link | parent | on: fork() in Arc

Thanks for the information on continuations and threads. Your suggestion worked perfectly for me.

And if you have to know, I am trying to port a program entered in the 5th annual Obfuscated Perl Contest (http://perl.plover.com/obfuscated/). It was originally implemented using fork(), but I was experimenting with using continuations and threads just to see if it would work.

-----

2 points by kens 6535 days ago | link

I took a look at the Perl program. Just a warning: Arc doesn't have real pipes, so if you try to synchronize on pipes like the Arc program, you're in for trouble. Also, if you implement real forks and fork 32 mzscheme interpreters in parallel, you better have a bigger computer than mine :-)

-----

2 points by almkglor 6535 days ago | link

I also added pipes to Anarki.

  (let (input-end output-end) (pipe)
    (...do what you will...))

-----

1 point by eds 6536 days ago | link | parent | on: Suggestion for two core features

This works, but do note that 'compose can take multiple arguments as in http://arclanguage.org/item?id=6121.

-----

3 points by eds 6538 days ago | link | parent | on: Suggestion for two core features

Actually, (2) is quite trivial (in Anarki):

  (defcall int (n f)
    (eval (cons 'compose (n-of n f))))
But of course this means you can't use the current version of infix math at the same time.

-----

1 point by eds 6538 days ago | link | parent | on: fork() in Arc

Also, I think I need to use wait() to wait for a child process to terminate, but I am somewhat at a loss as to how to accomplish this, since the manual says it takes an int pointer... Again, any help would be appreciate.

-----

2 points by stefano 6537 days ago | link

Try:

  (cdef _wait "wait" cint (cptr))

  (def wait (i)
    (let pi (cmalloc 4)
      (cpset pi cint i)
      (_wait pi)))
This creates a pointer, assign it a value and returns the value returned by _wait.

-----

1 point by eds 6532 days ago | link

Also, in your malloc() example, don't you have to explicitly free the memory afterwards? MzScheme's GC doesn't deal with memory explicitly allocated from C, does it? (I know the example itself uses only a tiny amount of memory, but still...)

-----

1 point by sacado 6532 days ago | link

Yes and no. I think mzscheme's GC can handle it without explicitly calling free at the right time, but you have to register a "finalizer" function to do so. That function will be called when the GC collects the object.

It does not know the size of malloced objects however, so be careful (no pb there, but if you allocate something very big, mzscheme will only see a new reference and will not necessarily call GC when you will actually lack memory).

-----

3 points by stefano 6532 days ago | link

If you alloc memory with cmalloc mzscheme will automatically delete it for you. Try

  (def infinite ()
    (cmalloc 4)
    (infinite))

  (infinite)
You shouldn't run out of memory. If you do, then I'm wrong.

-----

1 point by eds 6536 days ago | link

Just wondering: is there a way to malloc(sizeof(int)) from inside Arc instead of hard coding the size of an int?

-----

2 points by stefano 6535 days ago | link

You can make a C function like this:

  int size_of_int ()
  {
    return sizeof(int);
  }
and import it. I've followed this route while writing gtk.arc, but probably there's a better way to do it.

-----

2 points by eds 6538 days ago | link | parent | on: fork() in Arc

Thanks for your suggestion.

I do have one question about loading libc. Since the system doesn't automatically find libc.so for you, you need to name the path explicitly. What worked for me was

  (w/ffi "/lib/libc.so.6"
    (cdef fork "fork" cint ()))
but I don't really know if this is the right way to do it. Is there a better way?

-----

2 points by elibarzilay 6538 days ago | link

The underlying foreign implementation is looking for the library. The problem is that /usr/lib/libc.so is not the library and dlopen() does not know how to handle this.

A better alternative with mzscheme's foreign interface is to use `#f' for the library -- that treats the mzscheme binary as the library to open (which includes the usual libc stuff.)

-----

1 point by sacado 6538 days ago | link

Interesting. Should be added to ffi.arc...

-----

2 points by eds 6538 days ago | link

Yeah, because currently if you attempt

  (w/ffi #f
    (cdef cfork "fork" cint ()))
arc will convert #f to nil, which mzcheme thinks is an unbound variable. There are work-arounds, like

  (w/ffi (read "#f")
    (cdef cfork "fork" cint ()))
but this is rather ugly.

-----

3 points by eds 6539 days ago | link | parent | on: OOP in Arc

I believe that Arc intends to provide features sometimes considered to be OO, but not packaged in a single monolithic system like most OO langauges. So while you should (at least theoretically) be able to accomplish anything in Arc you could do in an OO language, don't expect it to conform to your concept of OO.

For example, see the discussion http://arclanguage.org/item?id=4855.

-----


I would have expected 'iso to work for that, but it also returns nil.

  arc> (iso 0 0.0)
  nil

-----

2 points by eds 6539 days ago | link | parent | on: Arc on Windows, trouble installing

You can solve the cwd problem by passing a "--load-cd" parameter to mzscheme:

  mzscheme --no-init-file --mute-banner --load-cd C:\path\to\arc\as.scm

-----

3 points by eds 6541 days ago | link | parent | on: Reducing Parentheses

> Compose does not work with macros.

Exactly what do mean? Macros seem to work in functional position at least.

  arc> (and:or nil t)
  t
> But really, what improvement in readability do you get with the 'block style?

I agree with you on this. 'block doesn't really remove parens, it just moves them around and removes the nested structure. According to pg's Ansi Common Lisp, "Lisp programmers read and write code by indentation, not by parentheses." I find nested code to be more readable than flat code for exactly that reason, even if the parens stack up at the end of the block.

-----

1 point by almkglor 6540 days ago | link

This is because the expression:

  (and:or nil t)
expands to:

  (and (or nil t))
But the expression:

  (idfn and:or)
Expands to:

  (idfn (compose and or))
If you read my comment well, you'll see that I specifically mentioned that:

> You see, foo:bar in function position is handled specially:

-----

2 points by absz 6540 days ago | link

Actually, I believe what's happening is that

  (and:or nil t)
becomes

  ((compose and or) nil t)
, which is then expanded to

  (and (or nil t))
by ac.scm. Observe:

  arc> ((compose and or) nil t)
  t

-----

1 point by absz 6541 days ago | link

As almkglor observed, compose is handled specially in functional position, being expanded during compilation. Observe:

  arc> (and:or '(nil t))
  (nil t)
  arc> (apply and:or '(nil t))
  Error: "Function call on inappropriate object #3(tagged mac #<procedure>) (nil t)"

-----

2 points by eds 6541 days ago | link

Which is exactly what I meant by "macros seem to work in functional position." But amkglor's original statement "compose does not work with macros" does not take into account the special treatment of 'compose in functional position, which is why I was confused.

And if I am not mistaken, first class macros, once implemented, could be used in other situations like your example above.

-----

3 points by absz 6541 days ago | link

Looking at alkmglor's comment, it appears to indicate that a literal compose doesn't work; when it's optimized out, it works fine. In other words, everyone is vehemently agreeing with each other ;)

And of course, first class macros could do this. But we don't have those yet…

-----

More