<commit_before>package water;

import java.util.Arrays;
import water.api.DocGen;
import water.api.Request.API;
import water.util.Log;

/**
 * Lockable Keys - locked during long running jobs, to prevent overwriting
 * in-use keys.  e.g. model-building: expected to read-lock input ValueArray &
 * Frames, and write-lock the output Model.  Parser should write-lock the
 * output VA/Frame, to guard against double-parsing.
 *
 * Supports:
 *   lock-and-delete-old-and-update (for new Keys)
 *   lock-and-delete                (for removing old Keys)
 *   unlock
 *
 * @author <a href="mailto:<EMAIL>"></a>
 * @version 1.0
 */
public abstract class Lockable<T extends Lockable<T>> extends Iced {
  static final int API_WEAVER = 1; // This file has auto-gen'd doc & json fields
  static public DocGen.FieldDoc[] DOC_FIELDS; // Initialized from Auto-Gen code.

  /** The Key being locked */
  @API(help="My Key")
  public final Key _key;

  /** Write-locker job  is  in _jobs[0 ].  Can be null locker.
   *  Read -locker jobs are in _jobs[1+].
   *  Unlocked has _jobs equal to null.
   *  Only 1 situation will be true at a time; atomically updated.
   *  Transient, because this data is only valid on the master node.
   */
  @API(help="Jobs locking this key")
  public transient Key _lockers[];

  // Create unlocked
  public Lockable( Key key ) { _key = key; }

  // -----------
  // Atomic create+overwrite of prior key.
  // If prior key exists, block until acquire a write-lock.
  // The call delete_impl, removing all of a prior key.
  // The replace this object as the new Lockable, still write-locked.
  // "locker" can be null, meaning the special no-Job locker; for use by expected-fast operations
  //
  // Example: write-lock & remove an old VA, and replace with a new locked Frame
  //     Local-Node                              Master-Node
  // (1)  FR,VA      -->write_lock(job)-->          VA
  // (2)  FR,VA.waiting...                       FR,VA+job-locked atomic xtn loop
  // (3)                                            VA.delete_impl onSuccess
  // (4)  FR         <--update success <--       FR+job-locked

  // Write-lock 'this', returns OLD guy
  public Lockable write_lock( Key job_key ) {
    Log.debug(Log.Tag.Sys.LOCKS,"write-lock "+_key+" by job "+job_key);
    return ((PriorWriteLock)new PriorWriteLock(job_key).invoke(_key))._old;
  }
  // Write-lock 'this', delete any old thing, returns NEW guy
  public T delete_and_lock( Key job_key ) {
    Lockable old =  write_lock(job_key);
    if( old != null ) {
      Log.debug(Log.Tag.Sys.LOCKS,"lock-then-clear "+_key+" by job "+job_key);
      old.delete_impl(new Futures()).blockForPending();
    }
    return (T)this;
  }

  // Obtain the write-lock on _key, which may already exist, using the current 'this'.
  private class PriorWriteLock extends TAtomic<Lockable> {
    final Key _job_key;         // Job doing the locking
    Lockable _old;              // Return the old thing, for deleting later
    PriorWriteLock( Key job_key ) { _job_key = job_key; }
    @Override public Lockable atomic(Lockable old) {
      _old = old;
      if( old != null ) {       // Prior Lockable exists?
        assert !old.is_wlocked() : "Key "+_key+" already locked; lks="+Arrays.toString(old._lockers); // No double locking by same job
        if( old.is_locked(_job_key) ) // read-locked by self? (double-write-lock checked above)
          old.set_unlocked(old._lockers,_job_key); // Remove read-lock; will atomically upgrade to write-lock
        if( !old.is_unlocked() ) // Blocking for some other Job to finish???
          throw new IllegalArgumentException(old.errStr()+" "+_key+" is already in use.  Unable to use it now.  Consider using a different destination name.");
      }
      // Update & set the new value
      set_write_lock(_job_key);
      return Lockable.this;
    }
  }

  // -----------
  // Atomic lock & remove self.  Nothing remains when done.
  public void delete( ) { 
    if( _key != null ) {
      Log.debug(Log.Tag.Sys.LOCKS,"lock-then-delete "+_key);
      new WriteDeleteLock().invoke(_key);
    }
    Futures fs = new Futures();
    delete_impl(fs);
    if( _key != null ) DKV.remove(_key,fs); // Delete self also
    fs.blockForPending();
  }
  public static void delete( Key k ) {
    if( k == null ) return;
    Value val = DKV.get(k);
    if( !val.isLockable() ) UKV.remove(k); // Simple things being deleted
    else ((Lockable)val.get()).delete();   // Lockable being deleted
  }

  // Obtain the write-lock on _key, then delete.
  private class WriteDeleteLock extends TAtomic<Lockable> {
    Lockable _old;              // Return the old thing, for deleting later
    @Override public Lockable atomic(Lockable old) {
      if( old == null ) return null; // No prior key to delete?
      _old = old;
      if(  !old.is_unlocked() ) // Blocking for some other Job to finish???
        throw new IllegalArgumentException(old.errStr()+" "+_key+" is in use.  Unable to delete now.");
      // Update-in-place the defensive copy
      old.set_write_lock(null);
      return old;
    }
  }

  // -----------<commit_msg>Missing NPE check<commit_after>package water;

import java.util.Arrays;
import water.api.DocGen;
import water.api.Request.API;
import water.util.Log;

/**
 * Lockable Keys - locked during long running jobs, to prevent overwriting
 * in-use keys.  e.g. model-building: expected to read-lock input ValueArray &
 * Frames, and write-lock the output Model.  Parser should write-lock the
 * output VA/Frame, to guard against double-parsing.
 *
 * Supports:
 *   lock-and-delete-old-and-update (for new Keys)
 *   lock-and-delete                (for removing old Keys)
 *   unlock
 *
 * @author <a href="mailto:<EMAIL>"></a>
 * @version 1.0
 */
public abstract class Lockable<T extends Lockable<T>> extends Iced {
  static final int API_WEAVER = 1; // This file has auto-gen'd doc & json fields
  static public DocGen.FieldDoc[] DOC_FIELDS; // Initialized from Auto-Gen code.

  /** The Key being locked */
  @API(help="My Key")
  public final Key _key;

  /** Write-locker job  is  in _jobs[0 ].  Can be null locker.
   *  Read -locker jobs are in _jobs[1+].
   *  Unlocked has _jobs equal to null.
   *  Only 1 situation will be true at a time; atomically updated.
   *  Transient, because this data is only valid on the master node.
   */
  @API(help="Jobs locking this key")
  public transient Key _lockers[];

  // Create unlocked
  public Lockable( Key key ) { _key = key; }

  // -----------
  // Atomic create+overwrite of prior key.
  // If prior key exists, block until acquire a write-lock.
  // The call delete_impl, removing all of a prior key.
  // The replace this object as the new Lockable, still write-locked.
  // "locker" can be null, meaning the special no-Job locker; for use by expected-fast operations
  //
  // Example: write-lock & remove an old VA, and replace with a new locked Frame
  //     Local-Node                              Master-Node
  // (1)  FR,VA      -->write_lock(job)-->          VA
  // (2)  FR,VA.waiting...                       FR,VA+job-locked atomic xtn loop
  // (3)                                            VA.delete_impl onSuccess
  // (4)  FR         <--update success <--       FR+job-locked

  // Write-lock 'this', returns OLD guy
  public Lockable write_lock( Key job_key ) {
    Log.debug(Log.Tag.Sys.LOCKS,"write-lock "+_key+" by job "+job_key);
    return ((PriorWriteLock)new PriorWriteLock(job_key).invoke(_key))._old;
  }
  // Write-lock 'this', delete any old thing, returns NEW guy
  public T delete_and_lock( Key job_key ) {
    Lockable old =  write_lock(job_key);
    if( old != null ) {
      Log.debug(Log.Tag.Sys.LOCKS,"lock-then-clear "+_key+" by job "+job_key);
      old.delete_impl(new Futures()).blockForPending();
    }
    return (T)this;
  }

  // Obtain the write-lock on _key, which may already exist, using the current 'this'.
  private class PriorWriteLock extends TAtomic<Lockable> {
    final Key _job_key;         // Job doing the locking
    Lockable _old;              // Return the old thing, for deleting later
    PriorWriteLock( Key job_key ) { _job_key = job_key; }
    @Override public Lockable atomic(Lockable old) {
      _old = old;
      if( old != null ) {       // Prior Lockable exists?
        assert !old.is_wlocked() : "Key "+_key+" already locked; lks="+Arrays.toString(old._lockers); // No double locking by same job
        if( old.is_locked(_job_key) ) // read-locked by self? (double-write-lock checked above)
          old.set_unlocked(old._lockers,_job_key); // Remove read-lock; will atomically upgrade to write-lock
        if( !old.is_unlocked() ) // Blocking for some other Job to finish???
          throw new IllegalArgumentException(old.errStr()+" "+_key+" is already in use.  Unable to use it now.  Consider using a different destination name.");
      }
      // Update & set the new value
      set_write_lock(_job_key);
      return Lockable.this;
    }
  }

  // -----------
  // Atomic lock & remove self.  Nothing remains when done.
  public void delete( ) { 
    if( _key != null ) {
      Log.debug(Log.Tag.Sys.LOCKS,"lock-then-delete "+_key);
      new WriteDeleteLock().invoke(_key);
    }
    Futures fs = new Futures();
    delete_impl(fs);
    if( _key != null ) DKV.remove(_key,fs); // Delete self also
    fs.blockForPending();
  }
  public static void delete( Key k ) {
    if( k == null ) return;
    Value val = DKV.get(k);
    if( val == null ) return;              // Or just nothing there to delete
    if( !val.isLockable() ) UKV.remove(k); // Simple things being deleted
    else ((Lockable)val.get()).delete();   // Lockable being deleted
  }

  // Obtain the write-lock on _key, then delete.
  private class WriteDeleteLock extends TAtomic<Lockable> {
    Lockable _old;              // Return the old thing, for deleting later
    @Override public Lockable atomic(Lockable old) {
      if( old == null ) return null; // No prior key to delete?
      _old = old;
      if(  !old.is_unlocked() ) // Blocking for some other Job to finish???
        throw new IllegalArgumentException(old.errStr()+" "+_key+" is in use.  Unable to delete now.");
      // Update-in-place the defensive copy
      old.set_write_lock(null);
      return old;
    }
  }

  // -----------
{'fraction_non_alphanumeric': 0.12381413359148112, 'fraction_numerical': 0.001936108422071636, 'mean_word_length': 2.47027208599261, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '107131', 'n_tokens_mistral': 3216, 'n_tokens_neox': 3155, 'n_words': 1058}