GAURAV BHATNAGAR wrote:
Now I am trying to call local variable messageCount from a private method but I am getting RuleCheckMethodAdapter.checkBindings : invalid local variable binding $messageCount checking method workingProducer()Ljava/lang/Object; for variable $messageCount
There is a reason for that :-). Here is your code formatted a bit more clearly (with line numbers):
private static Object workingProducer() throws InterruptedException {
String producerThreadName = Thread.currentThread().getName();
System.out.println("Producer Thread..." + producerThreadName);
int messageCount = 0;
while (true) {
blockingQueue.put(messageCount++);
System.out.println("Thread " + producerThreadName + " inserting " + messageCount + " into the blocking Queue");
Thread.sleep(1000);
}
and here is your RULE (I have added a missing semicolon and a comment character for your commewnt
RULE run producer then halt but consumers continue to run
CLASS ThreadInteraction
METHOD workingProducer
AFTER WRITE $producerThreadName
IF $messageCount = 6
DO traceln("with message count"+$messageCount);
waitFor($threadName) # (I want to pause the thread ..don't know how ...)
ENDRULE
The rule uses location AFTER WRITE $producerThreadName i.e. the point where local variable producerThreadName is first written, So, that is line 2 of the listing above. It is trying to refer to local variable messageCount. Now that variable is only declared at line 4. So, at the point where you are trying to inject the rule code variable messageCount does not exist.You are asking Byteman to modify the bytecode so it looks like it comes from source code that was something like this:
private static Object workingProducer() throws InterruptedException {
String producerThreadName = Thread.currentThread().getName();
if (messageCount == 6) {
traceln("with message count"+$messageCount);
waitFor($threadName); // (I want to pause the thread ..don't know how ...)
}
System.out.println("Producer Thread..." + producerThreadName);
int messageCount = 0;
while (true) {
blockingQueue.put(messageCount++);
System.out.println("Thread " + producerThreadName + " inserting " + messageCount + " into the blocking Queue");
Thread.sleep(1000);
}
That wouldn't get past a java compiler because at line 3 you are referring to variable messageCount before it has been declared. So, Byteman is telling you the same thing.
What you really want to do is inject the rule into the while loop after the 6th message send. So, you need to use a different location
RULE run producer then halt but consumers continue to run
CLASS ThreadInteraction
METHOD workingProducer
AFTER call put
IF $messageCount = 6
DO traceln("with message count"+$messageCount);
waitFor($threadName) # (I want to pause the thread ..don't know how ...)
ENDRULE
This means the injected code will follow the put call inside the loop. After the 6th put call the value of messageCount will indeed be 6 so the rule will fire at that point.
However, that's not finished yet because your rule has another error -- well, two related errors actually. The first one is that you have called waitFor($threadName) but there is no such local variable. I think what you actually meant to use was $producerThreadName. That change will make your rule parse and type check ok but it probably won't allow your producer and consumer to meet up.
I am assuming that a similar rule in your consumer is going to call helper method signalWake(XXX) with some argument XXX. In order for a wait and a notify call to match each other and cause two threads to synchronize the argument passed to these methods has to be the same. Byteman creates a Waiter object when waitFor is called and it labels it using the input argument to waitFor. It then suspends the thread, hanging it off the Waiter object. When another thread calls signalWake it uses the input argument to look up the Waiter and restarts the suspended thread. If you don't use the same argument then the call to signalWake will not find the Waiter. So, if you pass the producer thread's name to the waitFor call in the producer then you will also need to pass the producer thread's name in the signalWake call made by the consumer thread. How is your consumer going to obtain the name of the producer thread?
What you really need is pass some value shared by both threads to the waitFor and signalWake call. The most obvious solution is to use a constant value like a String or integer. So, you could call waitFor("data read") in the producer rule and signalWake("data read") in the consumer rule. This ensures that the two threads are waiting on the same waiter.
That solution is ok so long as you only have one producer and one consumer thread. However, if you have several producer-consumer pairs then that's going to confuse things because both producers will be waiting on the same Waiter and both consumers will be signalling the same Waiter. This may well mean that the signals get mixed up and one of your waiters may end up waiting for ever.
Luckily, you already have what you need to resolve this problem. Each producer-consumer pair is shares a queue used to transfer the data. So, you can use that to label the Waiter. Here is a rule which will work for your producer and should allow you to provide an equivalent rule for your consumer.
RULE run producer then halt but consumers continue to run
CLASS ThreadInteraction
METHOD workingProducer
AFTER call put
IF $messageCount = 6
DO traceln("with message count"+$messageCount);
waitFor($this.blockingQueue) # label waiter with queue
ENDRULE
n.b. I have assumed that blockingQueue is a field of the ThreadInteraction class so I have used $this.blockingQueue to refer to the queue ($this identifies the ThreadInteraction instance that is executing method workingProducer so you can use the normal field accessor syntax to access field blockingQueue) . I hope that is correct. If not you may have to adjust the rule according to whatever definition you have for this value.
I'll leave you to try to write the corresponding rule to inject into the consumer method. Let me know if you get it working.
Also, here's a hint: you may want to look up the documentation of signalWake to check what the optional second argument is for. It's unlikely but you may experience occasional problems if you don't pass value true for this second argument (if you omit it the default is false).
regards,
Andrew Dinn