SPIN question - testing for a condition, and executing it at the same time
Jeff Haas
Posts: 421
in Propeller 1
I'm going through tutorial examples about setting the locks. There's one example that confused me, and after thinking about it, I still don't quite understand the rules for how Spin is interpreting the line of code.
Here's the example:
What this seems to do is a few things:
1. Attempt to set a lock
2. Test for the condition - Was a lock set in the pool?
3. If not, display an error.
Where I'm confused is the first line, which both sets and tests for the condition. I would have expected that you attempt to set the lock on one line, get the result (-1) back, and then evaluate the result on the next line.
The section of the manual on the IF statement gives this example, which is what I'm used to for an IF statement:
I can't find a section of the manual that explains how the use of parenthesis in a command both allows you to execute the command and test for it at the same time. Did I overlook something?
Jeff
Here's the example:
if (semID := locknew) == -1
debug.Str(String("Error, no locks!"))
debug.Str(String("Error, no locks!"))
What this seems to do is a few things:
1. Attempt to set a lock
2. Test for the condition - Was a lock set in the pool?
3. If not, display an error.
Where I'm confused is the first line, which both sets and tests for the condition. I would have expected that you attempt to set the lock on one line, get the result (-1) back, and then evaluate the result on the next line.
The section of the manual on the IF statement gives this example, which is what I'm used to for an IF statement:
if X > 10
!outa[0]
!outa[0]
I can't find a section of the manual that explains how the use of parenthesis in a command both allows you to execute the command and test for it at the same time. Did I overlook something?
Jeff
Comments
That code is exactly the same as :
The trick is in the parenthesis. Whatever is in the parenthesis is evaluated to some result before that result is used in further operations. In your example "semID := locknew" is in parenthesis and happens first.
I suppose the other conceptual "trick" is that the assignment ":=" is actually an operator like "+" or "-" or anyother. When the operation is done there is a result.
So what is the result of "semID := locknew"? Simply the new value of semID.
With that done we now have a result to compare with -1.
-Phil
This is all the same as the algebra you learned in school. With the twist that "=" in maths does not mean "assignment" and it does not produce a result.
I'm sure there is mention of "operator precedence" in the manual.
Can expressions other than an assignment be used in the parenthesis?
I went through the section in the Propeller manual on operator precedence, this specific case isn't explained like you guys did. It is covered in part in the section on LOCKNEW, etc. but not as an overall concept.
Thanks!
-Phil
My understanding, which may be incorrect, is that the two snippets:
and
are not equivalent.
The difference is that the former, with the assignment in flow context, evaluates the result of locknew whereas the latter evaluates the value of semID. They may yield different results should other threads of execution be writing to semID coincidently with the execution of this path.
It's also the case that they will also work differently is semID is allocated statically and of size byte or word as then semID is unsigned and the evaluation of the predicate always yields False.
In either case, while its controversial to some, the use of locknew and (and lockret, cognew and cogret) serve no useful purpose. It's far simpler to pass lock and cog IDs into a module than to manage and/or store returned values. The allocations can always be managed statically and the errors are never recoverable.
Nor would you do that -- obviously.
There is no cogret. You might want to get more Propeller experience under your belt before being so critical of it and of those who make positive contributions to the art. Perhaps then you could be among those contributors.
-Phil
You are right. There is no formal definition of Spin. Which makes writing a Spin compiler troublesome. I had to do a lot of trial and error testing just to create a PASM assembler that would understand Spin's DAT, CON, VAR block syntax. It's amazing that BST and HomeSpun did Spin compilation so well.
However do you have any code that demonstrates that your two examples may fail to behave as expected?
I have to dispute your claim that "the use of locknew and (and lockret, cognew and cogret) serve no useful purpose." They are the only mechanisms by which we can attain atomic operations with the Propeller and hence mutual exclusion between multiple readers and writers of a shared data area.
I misspoke when I said cogret, I clearly meant cogstop.
Whether or not one would "do that" is not the point. The point is that the two constructs are not equivalent in two different ways; their semantics are different. And because compilation and execution are exact sports, it's important that descriptions of compilation and execution bear some basis in fact. As I've stated previously and has been acknowledged by others here, inexact terminology and lax definitions are the bane of good results.
Saying that obviously no one would "do that" is just bizarre. Using a byte to hold four bits of information? I do that all the time. What's not obvious is that allocating a byte won't work.
It's a shame that critique is not seen as a positive contribution - as it's sorely needed herein. P2 development is a testament to that.
Too, there are many using the 4x and 1x serial drivers I posted months ago. If smaller, faster, bugs fixed, features added and better documented is not a valuable contribution, despite being used by several, then I'm at a complete loss for what does constitute positive contribution?
Moreso, it seems to me that both Phil and Heater are of the opinion that they make positive contributions but here, in this thread, their contributions are factually wrong.
Does NOT compare the contents of semID to -1. semID is written, not read. It is the value returned by locknew that is the left-hand side of the equality operator. They both stated otherwise and they are both incorrect. The following snippet is one example of how this can be observed.
And yet, my critique of their factually erroneous postings is, evidently, also not a positive contribution.
I don't think Phil or Heater are bad people or otherwise ill equipped - but I do know they are wrong. And I'd like to believe that the original poster actually deserved a factually correct answer. And I'd like to believe that factually correct answers are fundamentally more positive contributions than incorrect answers. But evidently, that's just not the case.
So tell me then, what does constitute a positive contribution?
At this point I cannot tell if you have a reading comprehension problem or purposely fishing for an argument with every post. I believe they call it "trolling" now a days.
Either way, no one is listening any more. Not until you quit with the personal attacks and start with some semblance of a rational post.
Once upon a time, I thought the same thing. However, it turns out I misunderstood the way that the locks are organized. Internally, locks are actually composed of two bit fields, not one. One of the bit fields is used for locknew/lockret and the other is used for lockset/lockclr. Here's the intended usage pattern:
Now, a couple notes:
This is not correct. According to BST at least, the value of locknew is stored in semID but left on the stack to be later compared to -1.
-Phil
What does the compiled code actually do?
I have not checked but I assume it looks like:
call locknew with result in temp
write temp to semID
compare temp with -1
Where temp is the top of stack. Or perhaps a register if this were not a stack machine.
Ah, he say, see you are wrong, it compares against the result of locknew not semID.
So what? They are the same value! Semantically it is the same. If it is not then the compiler is broken.
Ah, but what if another process (COG) can change semID?
So what? If that is possible your program is broken.
Consider: It can happen with such a broken program that locknew succeeds and you continue with the body of the "if" statement AND that some other process changes semID on you. You now have a corrupted value in semID which will cause your program to fail if it tries to use it later. I presume your program does use it later else why have it in the first place?
So, we see that it does not matter if the compiler actually does it's comparison against semID or locknew. The semantics are the same.
Underlying this thread is a question of programming style driven by different needs that are only now being voiced. On the one hand, someone might have a complete program where the programmer / designer has complete control. It makes sense to allocate resources (like locks) during global initialization or as global constants and pass those resources around for use. On the other hand (the usual case for the Propeller), a program makes use of some number of objects which provide some abstraction like a UART or VGA text display and may be available in several implementations with different resource needs. Each object allocates the resources it needs, hopefully describing in its documentation what's used. A VGA text display may use one, two, or more cogs. A UART might use one or two cogs. Either might use a lock. Ideally, the calling program doesn't need to know how many cogs or locks are used. Sometimes the calling program might want to know and can ask for that information. Both programming styles are valid and have advantages and disadvantages. Spin was designed for the 2nd style with its use of some aspects of object-oriented programming. Dynamic allocation of cogs and locks is natural and used widely in the Object Exchange.
I don't think I made any personal attacks there. The issue about the semantics of the the code in question are there no matter who says what.
I think you have nailed a point about the programming style there. Normally we are used to mixing and matching objects from here and there. In which case cognew/locknew make a lot of sense. Using objects from OBEX and elsewhere would be a lot more difficult without.
Conversely if you have written all the code in your program perhaps cognew/locknew are not necessary. You know what resources you use and when.
On the other hand, even if you do write all the code, if you have processes that are started in COGs dynamically as the program runs, depending on what inputs are coming, then you are back to needing cognew/locknew. Or you have to manage those allocations yourself.
I guess we don't have many programs like that.
Re: personal attacks ... nobody start!
Re: this thread ...
I've written a lot of Spin (and PASM) code, much of which has been posted either here or in the ObEx. I've hardly ever needed locks. I've always allocated cogs dynamically where needed (in the leaves of the object tree) using COGNEW and COGSTOP. When I've used locks, I've done the same, usually hiding the existence of the locks from the user of the object involved. That's the style I learned doing operating system and compiler development. Memory resource allocation is a bit different, particularly since Spin allocates memory statically (except for the stack). Sometimes I've implemented a simple memory allocator so I can defer assigning addresses of significant structures (large arrays, some buffers, etc.) until they're needed. It depends on the circumstances ... always tradeoffs involved.
On the one hand it has "objects", with methods, but they are static resources that cannot be passed around like other types. (Let's not get into that "what is a type argument")
On the other hand it has objects like COGs and LOCKs that are dynamically allocated, like locknew and cognew, and passed around.
Talking of C, even that has dynamic objects that can be created, used, released and destroyed. Like files, for example, that are "opened" and "closed". Rather like the Propeller's locks and cogs.
On a lighter note-
Would ((Lottery Numbers)) = Tomorrows draw?
Might be useful if it works?
((Tomorrow's draw)) = Lottery numbers
confused spin user