/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.job.internal;

import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.xwiki.stability.Unstable;

/**
 * A specific concurrency implementation for managing Semaphore with Read/Write lock capabilities.
 * This semaphore allows several process to access a resource in read only, but lock it for accessing it in write when
 * the defined pool size is reached.
 *
 * @version $Id$
 * @since 12.5RC1
 */
@Unstable
public class ReadWriteSemaphore
{
    private volatile int readCounter;
    private volatile int writeCounter;
    private final Semaphore semaphore;
    private final Lock writeCounterLock;
    private final Lock readCounterLock;

    /**
     * Create a semaphore with the given number of permits.
     * @param poolSize the number of permits to allow.
     */
    public ReadWriteSemaphore(int poolSize)
    {
        this.semaphore = new Semaphore(poolSize, true);
        this.writeCounterLock = new ReentrantLock(true);
        this.readCounterLock = new ReentrantLock(true);
        this.readCounter = 0;
        this.writeCounter = 0;
    }

    /**
     * Takes one permit on the semaphore for writing, but also takes as much permits as they are reader
     * so we only allow reader or other writer depending on the semaphore size.
     */
    public void lockWrite()
    {
        // Encapsulate the operations about write counter
        // with a lock to avoid concurrent access on it.
        this.writeCounterLock.lock();
        this.writeCounter++;
        this.writeCounterLock.unlock();
        this.semaphore.acquireUninterruptibly(this.readCounter + 1);
    }

    /**
     * Release a permit in the semaphore. If the writer counter is now empty, then we also release as many permits as
     * they are readers.
     */
    public void unlockWrite()
    {
        // Encapsulate the operations about write counter
        // with a lock to avoid concurrent access on it.
        this.writeCounterLock.lock();
        this.writeCounter--;
        this.writeCounterLock.unlock();

        // the access of the writerCounter here should be safe since it's volatile.
        if (this.writeCounter == 0) {
            this.semaphore.release(this.readCounter + 1);
        } else {
            this.semaphore.release();
        }
    }

    /**
     * Increment the reader counter, and takes a permit in the semaphore only if there is already at least one writer
     * on it: we ensure to block new readers if the semaphore is full already.
     */
    public void lockRead()
    {
        // Encapsulate the operations about read counter
        // with a lock to avoid concurrent access on it.
        this.readCounterLock.lock();
        this.readCounter++;
        this.readCounterLock.unlock();

        // the access of the writerCounter here should be safe since it's volatile.
        if (this.writeCounter > 0) {
            this.semaphore.acquireUninterruptibly();
        }
    }

    /**
     * Decrement the reader counter, and release a permit in the semaphore only if there is already at least one writer
     * because we took previously a permit for this reader.
     */
    public void unlockRead()
    {
        // Encapsulate the operations about read counter
        // with a lock to avoid concurrent access on it.
        this.readCounterLock.lock();
        this.readCounter--;
        this.readCounterLock.unlock();

        // the access of the writerCounter here should be safe since it's volatile.
        if (this.writeCounter > 0) {
            this.semaphore.release();
        }
    }
}

@@ -991,67 +991,28 @@
 ent.
-locks.Lock;%0Aimport java.util.concurrent.locks.ReentrantLock
+atomic.AtomicInteger
 ;%0A%0Ai
@@ -1405,28 +1405,35 @@
 private 
-volatile int
+final AtomicInteger
  readCou
@@ -1454,20 +1454,27 @@
 ate 
-volatile int
+final AtomicInteger
  wri
@@ -1526,89 +1526,8 @@
 ore;
-%0A    private final Lock writeCounterLock;%0A    private final Lock readCounterLock;
 %0A%0A  
@@ -1778,49 +1778,41 @@
 his.
-write
+read
 Counter
-Lock
  = new 
-ReentrantLock(true
+AtomicInteger(0
 );%0A 
@@ -1827,110 +1827,43 @@
 his.
-read
+write
 Counter
-Lock
  = new 
-ReentrantLock(true);%0A        this.readCounter = 0;%0A        this.writeCounter = 0
+AtomicInteger(0)
 ;%0A  
@@ -2113,218 +2113,41 @@
     
-// Encapsulate the operations about 
+this.
 write
- c
+C
 ounter
-%0A        // with a lock to avoid concurrent access on it.%0A        this.writeCounterLock.lock();%0A        this.writeCounter++;%0A        this.writeCounterLock.unlock
+.incrementAndGet
 ();%0A
@@ -2200,32 +2200,38 @@
 this.readCounter
+.get()
  + 1);%0A    %7D%0A%0A  
@@ -2435,306 +2435,45 @@
     
-// Encapsulate the operations about write counter%0A        // with a lock to avoid concurrent access on it.%0A        this.writeCounterLock.lock();%0A        this.writeCounter--;%0A        this.writeCounterLock.unlock();%0A%0A        // the access of the writerCounter here should be safe since it's volatile.
+this.writeCounter.decrementAndGet();%0A
 %0A   
@@ -2498,16 +2498,22 @@
 eCounter
+.get()
  == 0) %7B
@@ -2564,16 +2564,22 @@
 dCounter
+.get()
  + 1);%0A 
@@ -2907,302 +2907,44 @@
     
-// Encapsulate the operations about 
+this.
 read
- c
+C
 ounter
-%0A        // with a lock to avoid concurrent access on it.%0A        this.readCounterLock.lock();%0A        this.readCounter++;%0A        this.readCounterLock.unlock();%0A%0A        // the access of the writerCounter here should be safe since it's volatile.
+.incrementAndGet();%0A
 %0A   
@@ -2961,32 +2961,38 @@
 his.writeCounter
+.get()
  %3E 0) %7B%0A        
@@ -3296,302 +3296,44 @@
     
-// Encapsulate the operations about 
+this.
 read
- c
+C
 ounter
-%0A        // with a lock to avoid concurrent access on it.%0A        this.readCounterLock.lock();%0A        this.readCounter--;%0A        this.readCounterLock.unlock();%0A%0A        // the access of the writerCounter here should be safe since it's volatile.
+.decrementAndGet();%0A
 %0A   
@@ -3358,16 +3358,22 @@
 eCounter
+.get()
  %3E 0) %7B%0A

Allow to define the pool size of a GroupedJob