def lock(name="global", **opts)
      f = nil

      # If we don't have a block, then locking is useless, so ignore it
      return if !block_given?

      # This allows multiple locks in the same process to be nested
      return yield if @locks[name] || opts[:noop]

      # The path to this lock
      lock_path = data_dir.join("lock.#{name}.lock")

      @logger.debug("Attempting to acquire process-lock: #{name}")
      lock("dotlock", noop: name == "dotlock", retry: true) do
        f = File.open(lock_path, "w+")
      end

      # The file locking fails only if it returns "false." If it
      # succeeds it returns a 0, so we must explicitly check for
      # the proper error case.
      while f.flock(File::LOCK_EX | File::LOCK_NB) === false
        @logger.warn("Process-lock in use: #{name}")

        if !opts[:retry]
          raise Errors::EnvironmentLockedError,
            name: name
        end

        sleep 0.2
      end

      @logger.info("Acquired process lock: #{name}")

      result = nil
      begin
        # Mark that we have a lock
        @locks[name] = true

        result = yield
      ensure
        # We need to make sure that no matter what this is always
        # reset to false so we don't think we have a lock when we
        # actually don't.
        @locks.delete(name)
        @logger.info("Released process lock: #{name}")
      end

      # Clean up the lock file, this requires another lock
      if name != "dotlock"
        lock("dotlock", retry: true) do
          f.close
          begin
            File.delete(lock_path)
          rescue
            @logger.error(
              "Failed to delete lock file #{lock_path} - some other thread " +
              "might be trying to acquire it. ignoring this error")
          end
        end
      end

      # Return the result
      return result
    ensure
      begin
        f.close if f
      rescue IOError
      end
    end
def lock(name="global", **opts)
      f = nil

      # If we don't have a block, then locking is useless, so ignore it
      return if !block_given?

      # This allows multiple locks in the same process to be nested
      return yield if @locks[name] || opts[:noop]

      # The path to this lock
      lock_path = data_dir.join("lock.#{name}.lock")

      @logger.debug("Attempting to acquire process-lock: #{name}")
      lock("dotlock", noop: name == "dotlock", retry: true) do
        f = File.open(lock_path, "w+")
      end

      # The file locking fails only if it returns "false." If it
      # succeeds it returns a 0, so we must explicitly check for
      # the proper error case.
      while f.flock(File::LOCK_EX | File::LOCK_NB) === false
        @logger.warn("Process-lock in use: #{name}")

        if !opts[:retry]
          raise Errors::EnvironmentLockedError,
            name: name
        end

        sleep 0.2
      end

      @logger.info("Acquired process lock: #{name}")

      result = nil
      begin
        # Mark that we have a lock
        @locks[name] = true

        result = yield
      ensure
        # We need to make sure that no matter what this is always
        # reset to false so we don't think we have a lock when we
        # actually don't.
        @locks.delete(name)
        @logger.info("Released process lock: #{name}")
      end

      # Clean up the lock file, this requires another lock
      if name != "dotlock"
        lock("dotlock", retry: true) do
          f.close
          begin
            File.delete(lock_path)
          rescue
            @logger.error(
              "Failed to delete lock file #{lock_path} - some other thread " +
              "might be trying to acquire it. ignoring this error")
          end
        end
      end

      # Return the result
      return result
    ensure
      begin
        f.close if f
      rescue IOError
      end
    end
This acquires a process-level lock with the given name.

 The lock file is held within the data directory of this environment,
 so make sure that all environments that are locking are sharing
 the same data directory.

 This will raise Errors::EnvironmentLockedError if the lock can't
 be obtained.

 @param [String] name Name of the lock, since multiple locks can
   be held at one time.