Class: QuartzTorrent::PieceManager

Inherits:
Object
  • Object
show all
Defined in:
lib/quartz_torrent/filemanager.rb

Overview

A class that spawns a thread for performing PieceIO operations asynchronously. This class is what is used to read and write blocks of a torrent.

Defined Under Namespace

Classes: Result

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(baseDirectory, torrinfo, alertCallback = nil) ⇒ PieceManager

Create a new PieceManager that will map to files inside ‘baseDirectory’. Parameter ‘torrinfo’ should be a Metainfo::Info object (the info part of the metainfo). Parameter ‘alertCallback’ should be a Proc. It will be called when an operation is complete. The alerted code can then retrieve the events from the completed queue. This callback will be called from a different thread.



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/quartz_torrent/filemanager.rb', line 289

def initialize(baseDirectory, torrinfo, alertCallback = nil)
  @alertCallback = alertCallback
  @mutex = Mutex.new
  @results = []
  @requests = []
  # The progress of requests as they are being serviced, keyed by request id.
  @requestProgress = {}
  @progressMutex = Mutex.new
  @requestsSemaphore = Semaphore.new
  @resultsSemaphore = Semaphore.new
  @baseDirectory = baseDirectory
  @torrinfo = torrinfo
  @pieceIO = PieceIO.new(baseDirectory, torrinfo)
  @requestId = 0
  @logger = LogManager.getLogger("piecemanager")
  @torrentDataLength = torrinfo.dataLength
  @startedCondition = ConditionVariable.new
  @startedMutex = Mutex.new
  @state = :before_start
  startThread
end

Instance Attribute Details

#torrentDataLengthObject (readonly)

Returns the value of attribute torrentDataLength.



311
312
313
# File 'lib/quartz_torrent/filemanager.rb', line 311

def torrentDataLength
  @torrentDataLength
end

Instance Method Details

#checkPieceHash(pieceIndex) ⇒ Object

Validate that the hash of the downloaded piece matches the hash from the metainfo. The result is successful? if the hash matches, false otherwise. The data of the result is set to the piece index.



360
361
362
363
364
365
366
# File 'lib/quartz_torrent/filemanager.rb', line 360

def checkPieceHash(pieceIndex)
  id = returnAndIncrRequestId
  return id if @state == :after_stop
  @requests.push [id, :hash_piece, pieceIndex]
  @requestsSemaphore.signal
  id
end

#findExistingPiecesObject

This is meant to be called when the torrent is first loaded to check what pieces we’ve already downloaded. The data property of the result for this call is set to a Bitfield representing the complete pieces.



349
350
351
352
353
354
355
# File 'lib/quartz_torrent/filemanager.rb', line 349

def findExistingPieces
  id = returnAndIncrRequestId
  return id if @state == :after_stop
  @requests.push [id, :find_existing]
  @requestsSemaphore.signal
  id
end

#flushObject

Flush to disk. The result for this operation is always successful.



369
370
371
372
373
374
375
# File 'lib/quartz_torrent/filemanager.rb', line 369

def flush()
  id = returnAndIncrRequestId
  return id if @state == :after_stop
  @requests.push [id, :flush]
  @requestsSemaphore.signal
  id
end

#hasResults?Boolean

Check if there are results ready. This method will return immediately without blocking.

Returns:

  • (Boolean)


409
410
411
# File 'lib/quartz_torrent/filemanager.rb', line 409

def hasResults?
  ! @results.empty?
end

#nextResultObject

Result retrieval. Returns the next result, or nil if none are ready. The results that are returned are PieceIOWorker::Result objects. For readBlock operations the data property of the result object contains the block.



381
382
383
384
385
386
387
388
# File 'lib/quartz_torrent/filemanager.rb', line 381

def nextResult
  result = nil
  @mutex.synchronize do
    result = @results.shift
    @progressMutex.synchronize{ @requestProgress.delete result.requestId } if result
  end
  result
end

#progress(requestId) ⇒ Object

Get the progress of the specified request as an integer between 0 and 100. Currently, only the findExistingPieces operation registers progress; other operations just return nil for this.



393
394
395
396
397
# File 'lib/quartz_torrent/filemanager.rb', line 393

def progress(requestId)
  result = nil
  @progressMutex.synchronize{ result = @requestProgress[requestId] }
  result
end

#readBlock(pieceIndex, offset, length) ⇒ Object

Read a block from the torrent asynchronously. When the operation is complete the result is stored in the ‘results’ list. This method returns an id that can be used to match the response to the request. The readBlock and writeBlock methods are not threadsafe with respect to callers; they shouldn’t be called by multiple threads concurrently.



319
320
321
322
323
324
325
# File 'lib/quartz_torrent/filemanager.rb', line 319

def readBlock(pieceIndex, offset, length)
  id = returnAndIncrRequestId
  return id if @state == :after_stop
  @requests.push [id, :read_block, pieceIndex, offset, length]
  @requestsSemaphore.signal
  id
end

#readPiece(pieceIndex) ⇒ Object

Read a block of the torrent asynchronously.



337
338
339
340
341
342
343
# File 'lib/quartz_torrent/filemanager.rb', line 337

def readPiece(pieceIndex)
  id = returnAndIncrRequestId
  return id if @state == :after_stop
  @requests.push [id, :read_piece, pieceIndex]
  @requestsSemaphore.signal
  id
end

#stopObject

Stop the PieceManager.



414
415
416
417
418
419
420
# File 'lib/quartz_torrent/filemanager.rb', line 414

def stop
  waitUntilStarted
  @state = :after_stop
  id = returnAndIncrRequestId
  @requests.push [id, :stop]
  @requestsSemaphore.signal
end

#waitObject

Wait until the next result is ready. If this method is used it must always be called before nextResult. This is mostly useful for testing.



401
402
403
404
405
# File 'lib/quartz_torrent/filemanager.rb', line 401

def wait
  waitUntilStarted

  @resultsSemaphore.wait
end

#writeBlock(pieceIndex, offset, block) ⇒ Object

Write a block to the torrent asynchronously.



328
329
330
331
332
333
334
# File 'lib/quartz_torrent/filemanager.rb', line 328

def writeBlock(pieceIndex, offset, block)
  id = returnAndIncrRequestId
  return id if @state == :after_stop
  @requests.push [id, :write_block, pieceIndex, offset, block]
  @requestsSemaphore.signal
  id
end