Synchronization

Roy Wilson designrw at bellatlantic.net
Mon May 15 14:57:12 PDT 2000


Hi,

The code I provided Patrick was developed  by Peter Welch at the University of Kent at
Canterbury. It was intended to illustrate just what Pat notes: the looseness of the Java
specification. Welch suggests tightening up the spec by using a library based on CSP.

Attached is my rewrite of the code using the JCSP library.

Roy Wilson
designrw at bellatlantic

Patrick Tullmann wrote:

> Roy Wilson <designrw at bellatlantic.net> pointed out to me in private
> email that Kaffe starves out threads that are contending for a lock.
> (I've attached his sample testcase --- somewhat modified).
>
> This behavior results from Kaffe's lock queues being LIFO.  Making the
> lock queue FIFO fixes his particular problem.  (I've attached the
> patch, too.)  The patch makes the releaser of a lock wake up the
> thread on the tail of the queue vs. the thread on the head of the
> queue.
>
> However, Godmar has pointed out to me that Kaffe's current
> implementation is within the specification (the spec makes no fairness
> or liveness guarantees).  Additionally, Kaffe breaks broken code
> "faster".  If you really want fairness, then you'll have to implement
> a queue of some sort above the Java primitives.
>
> Anyone else have thoughts on this (or references to JDC
> tips/bugs/documentation)?
>
> -Pat
>
> ----- ----- ---- ---  ---  --   -    -      -         -               -
> Pat Tullmann                                       tullmann at cs.utah.edu
>           This signature witticism intentionally left blank.
>
>  --8<---  TakeResource.Java
>
> class TakeResource {
>   public static final int HOLD_DELAY = 3000;
>   public static final int ACQUIRE_DELAY = 1000;
>   public static final int THREADSTART_DELAY = 1000;
>
>   public static void main (String argv[]) {
>     Resource resource = new Resource();
>     User[] user = new User[3];
>     user[0] = new User ("A  ", resource);
>     try { Thread.sleep(THREADSTART_DELAY); }
>     catch (InterruptedException e){}
>     user[1] = new User (" B ", resource);
>     try { Thread.sleep(THREADSTART_DELAY); }
>     catch (InterruptedException e){}
>     user[2] = new User ("  C", resource);
>   }
> }
>
> class User extends Thread {
>   private String id;
>   private Resource resource;
>   public User (String id, Resource resource) {
>     this.id = id;
>     this.resource = resource;
>     System.out.println ("User " + id + " started");
>     start();
>   }
>   public void run() {
>     while (true) {
>       resource.take(id);
>       try { Thread.sleep(TakeResource.ACQUIRE_DELAY); }
>       catch (InterruptedException e){}
>     }
>   }
> }
>
> class Resource {
>   public synchronized void take (String id) {
>     System.out.println ("User " + id + " has resource");
>     try { Thread.sleep(TakeResource.HOLD_DELAY); }
>     catch (InterruptedException e){}
>   }
> }
>
>  --8<---  TakeResource.Java
>
>  --8<---  Patch
>
> Index: kaffe/kaffevm/locks.c
> ===================================================================
> RCS file: /cvs/kaffe/kaffe/kaffe/kaffevm/locks.c,v
> retrieving revision 1.35
> diff -u -r1.35 locks.c
> --- kaffe/kaffevm/locks.c       2000/04/12 17:41:06     1.35
> +++ kaffe/kaffevm/locks.c       2000/05/15 18:59:11
> @@ -248,12 +248,43 @@
>          * time to tell them.
>          */
>         if (lk->mux != 0) {
> +#if 1 /* FAIRQUEUING */
> +               /* Special case: a single element list */
> +               if (unhand(lk->mux)->nextlk == NULL)
> +               {
> +                       tid = lk->mux;
> +                       lk->mux = NULL;
> +               }
> +               else
> +               {
> +                       Hjava_lang_Thread** prevTail;
> +
> +                       prevTail = &lk->mux;
> +
> +                       /* while there are still two more on the list */
> +                       while((unhand(*prevTail)->nextlk != NULL)
> +                             && (unhand(unhand(*prevTail)->nextlk)->nextlk) != NULL) {
> +                               prevTail = &(unhand(*prevTail)->nextlk);
> +                       }
> +
> +                       tid = unhand(*prevTail)->nextlk;
> +                       assert(unhand(tid)->nextlk == NULL);
> +                       unhand(*prevTail)->nextlk = NULL;
> +
> +                       /* lk->mux is head of queue, don't update */
> +               }
> +
> +               lk->holder = NULL;
> +               putHeavyLock(lkp, lk);
> +               ksemPut((Ksem*)(unhand(tid)->sem));
> +#else
>                 tid = lk->mux;
>                 lk->mux = unhand(tid)->nextlk;
>                 unhand(tid)->nextlk = 0;
>                 lk->holder = 0;
>                 putHeavyLock(lkp, lk);
>                 ksemPut((Ksem*)(unhand(tid)->sem));
> +#endif
>         }
>         /* If someone's waiting to be signaled keep the heavy in place */
>         else if (lk->cv != 0) {
>
>  --8<---  Patch
-------------- next part --------------
// This code involves minimal change to the original version, and guarantees
// fair thread scheduling by both SUN JDK 1.2.1 and  Kaffe 1.05 JVM's.
// This is made possible by the JCSP (Java Communicating Sequential Processes) 
// package developed at the University of Kent at Canterbury by P. Welch
// and P. Austin.
//
// The following code was created using SUN Forte for Java CE v1.0
// by Roy  Wilson, designrw at bellatlantic.net on 10 May 2000
// Although not shown, each file was declared as part of a package.

import jcsp.lang.*;
class TakeResource {
  public static void main (String argv[]) {
    Resource resource = new Resource();

// User is a JCSP-defined CSProcess, an active object that encapsulates its own thread(s)
    User[] user = new User[3];
    user[0] = new User ("A  ", resource);
    user[1] = new User (" B ", resource);
    user[2] = new User ("  C", resource);

// The JCSP-defined Parallel construct causes the User processes to execute concurrently

    new Parallel( user ).run();
  }
}

import jcsp.lang.*;
import java.util.Random;
public class User implements CSProcess {
  private String id;
  private Resource resource;

  public User (String id, Resource resource) {
    this.id = id;
    this.resource = resource;
    System.out.println ("User " + id + " started");
  }

  public void run() {
    final Timer tim = new Timer ();
 
// As before, each User process takes control of the Resource object.
// The thread encapsulated within the User then sleeps.    
    while (true) {
	resource.take(id);
	tim.sleep(1000);
    }
  }
}

import jcsp.lang.*;
class Resource {
  public void take(String id) {
      Timer tim = new Timer ();

      System.out.println ("User " + id + " has resource");
// Once a User process has acquired the Resource object, it's thread
// sleeps for 3000 time units 
      tim.sleep(3000);
  }
}



More information about the kaffe mailing list