Program does not exit after Assert() implied fact using CreateFactBySize

43 views
Skip to first unread message

Ryan Johnston

unread,
Apr 17, 2023, 7:47:26 PM4/17/23
to CLIPSESG
I'm attempting to do something like the following:

  Fact *fact, *rv;
  GCBlock gcb;                                         
  int danglingConstructs;                                         
  fact = CreateFactBySize(env, 1);                                                                                  
  fact->whichDeftemplate = (Deftemplate *) CreateImpliedDeftemplate(env, CreateSymbol(env, first_field), true);      
  IncrementClearReadyLocks(env);                                                                                    
  //
  MultifieldBuilder *mb = CreateMultifieldBuilder(env, 3);                                                          
  CLIPSValue clips_value2;
  clips_value2.lexemeValue = CreateSymbol(env, "nil");                                                              
  MBAppend(mb, &clips_value2);                                                                                      
  clips_value2.lexemeValue = CreateSymbol(env, "asdf");                                                              
  MBAppend(mb, &clips_value2);                                                                                      
  clips_value2.lexemeValue = CreateSymbol(env, "hjkl");                                                              
  MBAppend(mb, &clips_value2);
  fact->theProposition.contents[0].multifieldValue = MBCreate(mb);                                                  
  MBDispose(mb);
  //
  DecrementClearReadyLocks(env);                                                                                    
   
  if (EvaluationData(env)->CurrentExpression == NULL)
    { ResetErrorFlags(env); }                                                                                        
   
  GCBlockStart(env,&gcb);
   
  if (EvaluationData(env)->CurrentExpression == NULL)                                                                
    { ConstructData(env)->DanglingConstructs = danglingConstructs; }                                                
   
  rv = Assert(fact);                                                                                                
     
  GCBlockEnd(env,&gcb);

However, when I run my program, it does not exit at the end. It DOES appear that the fact is asserted, and I can infact do other things within the program after this, but the program does not exit at the end; I need to ctrl+c to send an interrupt signal. Replacing the fact->theProposition.contents[0].multifieldValue = MBCreate(mb); line with fact->theProposition.contents[0].multifieldValue = MBCreate(mb); DOES cause the program to exit. I'm guessing I'm missing the need to clear a lock somewhere? Or perhaps I need to clean up the multifield somehow?


Ryan Johnston

unread,
Apr 17, 2023, 10:52:55 PM4/17/23
to CLIPSESG
Ok, I got this to work by changing the line to:

 fact->theProposition.contents[0].multifieldValue = CopyMultifield(env,  MBCreate(mb));

Very interested in learning how this works, if I'm doing something that's not such a good idea, etc.

CLIPS Support

unread,
Apr 18, 2023, 2:07:58 PM4/18/23
to CLIPSESG
If you want to build an ordered fact piecemeal, I'd recommend using a StringBuilder rather than using functions that aren't part of the documented CLIPS API:

  Fact *rv;
  StringBuilder *sb = CreateStringBuilder(env,256);
  SBAppend(sb,"(");
  SBAppend(sb,"my_fact");
  SBAppend(sb," ");
  SBAppend(sb,"nil");
  SBAppend(sb," ");
  SBAppend(sb,"asdf");
  SBAppend(sb," ");
  SBAppend(sb,"hjkl");
  SBAppend(sb,")");
  rv = AssertString(env,sb->contents);
  SBDispose(sb);

In your original code, you're calling MBCreate to create the multifield for storing the fact's data, but this creates a structure that's managed by the periodic garbage collector. That's probably what is causing your hang because the code for fact deletion handles getting rid of that structure, not the garbage collector. Your call to CopyMultifield creates a copy of the multifield created by MBCreate and that copy isn't managed by the garbage collector.  

Ryan Johnston

unread,
Apr 19, 2023, 10:51:44 PM4/19/23
to CLIPSESG
I can certainly build a string and then AssertString. The semi-tricky thing here is if I want one of the fields to be a string or symbol with spaces. In my program, I'm taking input from the user, so my SBAppend might look something like SBAppend(sb, user_input). This would of course work, but if there is a space character in user_input, I'd want to make this a single symbol. I could write some code that checks user_input for a space character, but I'm betting there's a better way. I could also surround it with SBAppend(sb, "(sym-cat ")? I suppose I could surround the multi-word string with SBAppend(sb, "\""), too.

CLIPS Support

unread,
Apr 20, 2023, 7:04:46 PM4/20/23
to CLIPSESG
This would also be easier to do using a deftemplate fact with a FactBuilder.

Ryan Johnston

unread,
Apr 23, 2023, 9:35:09 AM4/23/23
to CLIPSESG
Very true 😌 this is a good learning opportunity, though, and I'm having fun deciphering how CLIPS is built. I appreciate your patience in explaining! I've narrowed it down to a cleanupFunction run during DestroyEnvironment. I'm not quite sure which one it is, but the program runs it with (*theEnvironment->cleanupFunctions[i])(theEnvironment); and seems to hang in the execution of that function. I'm still debugging through it, and I know I'm off the documented API. At this point, this is more of a "satisfying my curiosity" kind of thing. I'll report back with any findings!

Ryan Johnston

unread,
Apr 24, 2023, 10:25:07 PM4/24/23
to CLIPSESG
Ok, I've found the behavior. In DeallocateUtilityData, the "Free up multifield data" while loops infinitely. I'll avoid doing this in my code, but would be interested in learning more about the garbage collection process to understand this behavior.

CLIPS Support

unread,
Apr 25, 2023, 1:05:19 PM4/25/23
to CLIPSESG
CLIPS stores the multifields subject to garbage collection on a linked list. If you add the same multifield twice, which is what is happening in this case, you create a loop. For example, if your list is initially this:

A --> B --> C --> D --> E

and you add C again, you end up with this:

C --> A --> B --> C...

This list now points to C and since the pointer to the next item for C is changed from D to A, items D and E are no longer part of the list and the prior link from B to C remains but takes you back to the beginning of the list.

Reply all
Reply to author
Forward
0 new messages