code: /*
   * Copyright 2012-2018 Chronicle Map Contributors
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package net.openhft.chronicle.map;
  
  import net.openhft.chronicle.core.Jvm;
  import net.openhft.chronicle.core.OS;
  import net.openhft.chronicle.map.utility.ProcessInstanceLimiter;
  
  import java.io.File;
  import java.io.IOException;
  import java.io.Serializable;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.concurrent.ConcurrentHashMap;
  
  // todo make this example more efficient e.g. not using Serializable.
  public class ProcessInstanceLimiterMain implements Runnable {
      private static final long TIME_UPDATE_INTERVAL = 100L;
      private final String sharedMapName;
      private final Map<String, Data> theSharedMap;
      private final Callback callback;
      private final Map<String, IntWrapper> localUpdates = new ConcurrentHashMap<String, IntWrapper>();
  
      public ProcessInstanceLimiterMain(String sharedMapName, Callback callback) throws IOException {
          this.sharedMapName = sharedMapName;
          this.callback = callback;
          ChronicleMapBuilder<String, Data> builder =
                  ChronicleMapBuilder.of(String.class, Data.class);
          builder.entries(10000);
          builder.minSegments(2);
          File file = new File(OS.getTarget() + "/" + sharedMapName);
          this.theSharedMap = builder.create();
          Thread t = new Thread(this, "ProcessInstanceLimiterMain updater");
          t.setDaemon(true);
          t.start();
      }
  
      public static void main(String[] args) throws IOException, InterruptedException {
          Callback callback = new Callback() {
              public void tooManyProcessesOfType(String processType) {
                  System.out.println("Too many processes of type " + processType + " have been started, so exiting this process");
                  System.exit(0);
              }
  
              public void noDefinitionForProcessesOfType(String processType) {
                  System.out.println("No definition for processes of type " + processType + " has been set, so exiting this process");
                  System.exit(0);
              }
          };
          ProcessInstanceLimiterMain limiter = new ProcessInstanceLimiterMain("test", callback);
          limiter.setMaxNumberOfProcessesOfType("x", 2);
          limiter.startingProcessOfType("x");
          Jvm.pause(60L * 1000L);
      }
  
      public static void pause(long pause) {
          ProcessInstanceLimiter.pause(pause);
      }
  
      public void run() {
          //every TIME_UPDATE_INTERVAL milliseconds, update the time
          while (true) {
              try {
                  pause(TIME_UPDATE_INTERVAL);
                  String processType;
                  for (Entry<String, IntWrapper> entry : this.localUpdates.entrySet()) {
                      processType = entry.getKey();
                      int index = entry.getValue().intValue;
                      Data data = this.theSharedMap.get(processType);
                      if (data != null) {
                          long timenow = System.currentTimeMillis();
                          data.time[index] = timenow;
                          this.theSharedMap.put(processType, data);
                          while ((data = this.theSharedMap.get(processType)).time[index] != timenow) {
                              data.time[index] = timenow;
                              this.theSharedMap.put(processType, data);
                          }
                      }
                  }
              } catch (Exception e) {
                  // TODO
                  e.printStackTrace();
              }
          }
      }
  
      public void startingProcessOfType(String processType) {
          Data data = this.theSharedMap.get(processType);
          if (data == null) {
              this.callback.noDefinitionForProcessesOfType(processType);
              this.callback.tooManyProcessesOfType(processType);
              return;
          }
          long[] times1 = data.time;
          pause(2L * TIME_UPDATE_INTERVAL);
          long[] times2 = this.theSharedMap.get(processType).time;
          for (int i = 0; i < times1.length; i++) {
              if (times2[i] == times1[i]) {
                  //we have an index which has not been updated in 200ms
                  //so we have a spare slot - use this slot
                  this.localUpdates.put(processType, new IntWrapper(i));
                  return;
              }
          }
          this.callback.tooManyProcessesOfType(processType);
      }
  
      public void setMaxNumberOfProcessesOfType(String processType, int maxNumberOfProcessesAllowed) {
          this.theSharedMap.put(processType, new Data(processType, maxNumberOfProcessesAllowed));
      }
  
      public interface Callback {
          public void tooManyProcessesOfType(String processType);
  
          public void noDefinitionForProcessesOfType(String processType);
      }
  
      public static class Data implements Serializable {
          /**
           * generated serialVersionUID
           */
          private static final long serialVersionUID = 9163018396438735118L;
          String processType;
          int maxNumberOfProcessesAllowed;
          long[] time;
  
          public Data(String processType, int maxNumberOfProcessesAllowed) {
              this.processType = processType;
              this.maxNumberOfProcessesAllowed = maxNumberOfProcessesAllowed;
              this.time = new long[maxNumberOfProcessesAllowed];
          }
      }
  
      public static class IntWrapper {
          int intValue;
  
          public IntWrapper(int intValue) {
              this.intValue = intValue;
          }
      }
  }
  