Counting Free Cogs

All,
I recently had a need for a function to return the current number of free cogs. Here is something I whipped up in SPIN that did what I needed:
Anybody?
I recently had a need for a function to return the current number of free cogs. Here is something I whipped up in SPIN that did what I needed:
CON
STACKSIZE = 4 ' what is the smallest size this could be?
VAR
LONG Status[ 8 ]
LONG Stack[ 8*STACKSIZE ]
'
' Set Status - set our value in the Status array to 1
'
PRI Set_Status
Status[ cogid ] := 1
' now just wait forever
repeat
'
' Start Set_Status in free cogs until we can't run any more, then count them
'
PUB Free_Cog_Count : count | i
repeat i from 0 to 7
Status[ i ] := 0
repeat i from 0 to 7
if cognew(Set_Status, @Stack[ STACKSIZE*i ]) < 0
' no more cogs, so ...
quit
repeat i from 0 to 7
if Status[ i ] > 0
cogstop(i)
count++
return count
I didn't need it to be very efficient, but it strikes me that this would be a useful function to keep in my box of 'Propeller tricks' - but those who are better at SPIN than I am should be able to do it in much less space (this version takes 96 code bytes, plus 40 longs!).Anybody?
Comments
var long stack[10], freecogs PUB dummy freecogs++ waitcnt(cnt+40_000_000) cogstop(cogid) PUB cogsfree freecogs:=0 repeat while cognew(dummy,@stack) => 0 return freecogs
-Phil
Neat idea - but all your instances of "dummy" are using the same stack. Won't this cause problems?
Also (and I know I didn't say this, but I should have) the counting function should not return till all the cogs are free again. The main reason you would want to check how many cogs are free is that you want to use one, but with this solution you would get told there are "n" cogs free, but when you actually try and use one there won't be any!
Ross.
Good point about returning.
var long freecogs,stack[32] PUB dummy freecogs++ waitcnt(cnt+40_000) cogstop(cogid) PUB cogsfree freecogs:=0 repeat while cognew(dummy,@stack[freecogs<<2]) => 0 waitcnt(cnt+80_000) return freecogs
pub FreeCogs : num num := 8 repeat num if cognew(@asmcog,0) < 0 num-- DAT org 0 asmcog add t1,cnt waitcnt t1,#0 cogid t1 cogstop t1 t1 long 100_000
Andy
Thanks - this is more like the size I had in mind. But it suffers the same problem as Bill's solution - i.e. that you have to wait a certain amount of time after you have calculated the number of free cogs before any use can be made of this knowledge - because the cogs that are supposed to be free will not actually be available for use (in this case, for the next 100_000 cycles).
Perhaps we could instead take a slightly different approach - if the code returned the identity of the free cogs (in a bitset) rather than just the number of them, then programs could then use a coginit instead of a cognew.
Ross.
PUB FreeCogs : num | i,status,stat2 repeat i from 0 to 7 if (byte[@status][i] := cognew(@asmcog,0)) => 0 num++ repeat i from 0 to 7 if byte[@status][i] < 255 cogstop(byte[@status][i]) DAT asmcog jmp #asmcog
but needs 2 longs more
Andy
Nice!
PUB CountFreeCogs repeat 7 cognew(@gobble, 0) return 0 DAT gobble jmp #gobble
-Phil
You are not trying hard enough. You could make that faster by terminating the loop when cognew fails:-)
Does anyone know why I might think so?
[8^)
PUB FreeCogs | stayin_alive stayin_alive~~ repeat 7 result -= (cognew( @loiter_cog, @stayin_alive ) > -1) stayin_alive~ DAT loiter_cog rdlong tmp, par wz if_nz jmp #loiter_cog cogid tmp cogstop tmp tmp res 1
FreeCogs is 20 bytes, plus the 4 longs for the PASM code.Jonathan
I can not see any other way to get an accurate count given all situations. If more than one cog executes code to start and or stop a cog you can not have an accurate count.
EDIT: Oh, I now see that Phil's "solution" was intended as a joke.
PUB FreeCogs : num | i,status,stat2 repeat 7 if (i := cognew(@asmcog, 0)) < 0 quit byte[@status][num++] := i i := @status repeat num cogstop(byte[i++]) DAT asmcog jmp #asmcog
PUB FreeCogs : num | i,status,stat2 i := @status repeat while (byte[i][num++] := cognew(@asmcog, 0)) => 0 repeat --num cogstop(byte[i++]) DAT asmcog jmp #asmcog
That's some very elegant Spin!
-Phil
Well, I can't fault Phil's method for accuracy
Ross
CON _CLKMODE = XTAL1 + PLL16X _XINFREQ = 5_000_000 PUB START(RRR) COGINIT(7,@LOADAPP,@MAINAPP) DAT ORG 0 LOADAPP MOV GOCOG, PAR SHL GOCOG, #4 COGINIT GOCOG MOV BCOG, #7 COGSTOP BCOG BCOG LONG 0 GOCOG LONG 0 MAINAPP ORG 0 WRLONG COGCNT,#1 LOCKNEW COGLCK COGINC 'Code to include in all cogs. RDLONG COGLCKH, COGLCK '... your COG 0 Code. COGBGN 'Include in all cogs that start a cog ' and use instead of cognew. LOCKSET COGLCK WC IF_C JMP COGBGN RDLONG COGCNT, LCOGCNT CMP LCOGCNT, 8 WC, WZ IF_B MOV MGOCOG, NXTPAR IF_B SHL MGOCOG, #14 IF_B MOV MGOCOG, NXTASM 'Use your own ptrs. IF_B SHL MGOCOG, #4 IF_B OR MGOCOG, #8 IF_B COGINIT MGOCOG IF_B ADD LCOGCNT, #1 IF_B WRLONG COGCNT, LCOGCNT IF_AE MOV EE, #$FF LOCKCLR COGLCK RET COGSTP 'Include in all cogs that stop any cog ' and use instead of cogstop. LOCKSET COGLCK WC IF_C JMP COGBGN RDLONG COGCNT, LCOGCNT COGSTOP STCOG SUB LCOGCNT, #1 LOCKCLR COGLCK RET EE LONG 0 'Reg to track errors STCOG LONG 0 'Parameter for cogstp. NXTPAR LONG 0 'REPLACE WITH ACTUAL PAR TO PASS. NXTASM LONG $0000_7000 'Replace for your needs. COGCNT LONG $0000_7F00 LCOGCNT LONG 1 COGLCKH LONG $0000_7F04 COGLCK LONG 0 WCOG LONG 0 MGOCOG LONG 0
NOTE: THIS CODE IS UNTESTED. THIS IS THE FIRST TIME I HAVE USED THE LOCK OPS.
Please point out any optimizations and/or errors that I may have made.
This is fine if you are writing code completely from scratch - but most people make use of other code that already exists - and that code won't be quite so co-operative!
It would have been good if the Prop had included a built in register that showed the current run state of all cogs. But even that would be fooled on occasion (e.g. if a new cog is started between the time you checked the register and the time you tried to use what you thought was a free cog!).
I wonder if it's too late for the Prop II?
-Phil
Ah I could see this as a problem. I have always preferred to write my own code from the ground up only including existing routines as I must, and then I only use routines written by others if I truly do not understand the core concepts. This is why I have never liked the standard libraries of most programming languages.
Hi Phil,
I hate to tell you this, but I use coginit quite a lot - e.g. in the Catalyst loader.
For instance, some programs fail to run if they are run by a SPIN interpreter that is not running in cog 0.
Other times programs have to restart the cog they are currently running in.
Is there a reason not to use coginit?
Ross.
Different issue really. You already own the cog so no harm done.