0

I have a requirement to be able to count number of times AtomicReference[V].get is called in a class that has as field an array of wildcarded atomic references.

To that end, first, I've extended java's AtomicReference[V]:

import java.util.concurrent.atomic.{AtomicInteger => AInt, AtomicReference => ARef}

class MyAtomicReference[V] extends ARef[V]{

  private val getCounter: AInt = new AInt(0)

  def getAndListen(): V = {
    getCounter.getAndIncrement()
    super.get()
  }

  def counter(): Int = getCounter.get()

  def resetCounter(): Unit = getCounter.set(0)
}

Then I've added trait AtomicRefCounter which declares the method that I would wish to invoke:

import simulacrum.typeclass

 @typeclass trait AtomicRefCounter [R[_], T] {
  def countGets(container: R[T]): Int
}

Lastly, I've defined a default AtomicArrayRefCounter in the object DefaultAtomicRefCounters:

object DefaultAtomicRefCounters {

  implicit val arrayOfAtomicsTraverser = new AtomicRefCounter[Array, MyAtomicReference[_]] {
    override def countGets(container: Array[MyAtomicReference[_]]): Int = container map(_.counter()) sum
  }
}

Despite that when I try to call the traverseAtomics() on a corresponding array in a test, I do not see it (I am using Intellij IDEA):

behavior of "removeO1"

  "method" should "remove an element from the pool with time O(1)" in new IntPoolBuilder {
    import org.learningconcurrency.traditional_concurrency.helpers.DefaultAtomicRefCounters._
pool.buckets.countGet
      }

A piece of advice on what I am missing would really help. Usage of simulacrum is not mandatory - if you feel you know how to solve this without it, I would love to hear that.

update:

This is how the buckets are implemented:

class Pool[T] {

  type TimeStampedList = (List[T], Long)

  val parallelism: Int = Runtime.getRuntime.availableProcessors * 32
  val buckets = new Array[MyAtomicReference[TimeStampedList]](parallelism)
...
vasigorc
  • 882
  • 11
  • 22
  • 1. You named the function `countGets` but call `countGet`, which is probably just a typo in the question? 2. How is `pool.buckets` defined/initialized? – Sascha Kolberg Nov 26 '18 at 05:12
  • Hi Sascha, I've updated the ticket to include definition and initialization of `buckets`. `countGet` is not a typo, usually when you type a method in Intellij IDEA it prompts you with a matching member, but I assure you that `countGets` is not there neither. – vasigorc Nov 26 '18 at 13:45

2 Answers2

2

I think, you might have gotten wrong how implicits work.

If I read everything correctly, then in your code

implicitly[AtomicRefCounter[Array, MyAtomicReference[_]]].countGets(pool.buckets)

should work.

I you wanted to call countGets on the Array you should use the EnrichMyLibrary pattern.

object DefaultAtomicRefCounters {
  implicit class RichArray(private underlying: Array[MyAtomicReference[_]] extends AnyVal {
    def countGets: Int = underlying.map(_.counter()).sum
  }
}
Sascha Kolberg
  • 7,092
  • 1
  • 31
  • 37
0

As disappointing as it is, I couldn't make it work with simulacrum annotation, so I've followed Sascha's advise. I just modified slightly his second example (I couldn't get it to work with implictly) so it compiles and works:

object TraditionalConcurrencyHelpers {
  implicit class CountArrayAtomicGetsOps(wrapper: Array[MyAtomicReference[(List[Int], Long)]]) {
    def countGets()(implicit atomicRefCounter: AtomicRefCounter[Array, MyAtomicReference[(List[Int], Long)]]): Int = atomicRefCounter.countGets(wrapper)
  }
}

With this I have no problem calling countGets on the array:

  behavior of "removeO1"

  "method" should "remove an element from the pool with time O(1)" in new IntPoolBuilder {
    import TraditionalConcurrencyHelpers._
    import org.learningconcurrency.traditional_concurrency.helpers.DefaultAtomicRefCounters._
    //call pool.removeO1 (not implemented yet)
    pool.buckets.countGets() shouldEqual 1
  }
vasigorc
  • 882
  • 11
  • 22