cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Best Practice when using a Checkout Filter with DUP_GROUPing

Best Practice when using a Checkout Filter with DUP_GROUPing

Summary

This article will help to point out the best practices that can deployed when using a checkout filter with DUP_GROUP turned on.

Question

When using DUP_GROUP it is expected that a license checkout would be reused for the same license. However it has been observed that this dose not sometimes happen if a checkout filter is used.

If a checkout filter is used, even one as simple as the following code snippet:
int LM_CALLBACK_TYPE checkoutCallback(LM_HANDLE_PTR hjob, CONFIG_PTR cfg, PVOID param) {
    CONFIG *config, *pos = 0;
    
    cout << "Checkout post" << endl;

}


It has been observed that when multiple processes all using the same licenses are used and the maximum number of users have used up the licenses, then a -4 error will be displayed. This seems to be counter to the DUP_GROUP flag being used. Here are the steps to reproduce the issue:

1. Start the license server with this license file:
SERVER this_host ANY
VENDOR flynn
USE_SERVER
#a counted license
INCREMENT f1 flynn 2014.0101 permanent 2 \
    ISSUED=17-jul-2015 SIGN="102C AFE3 B0C3 DC0A 8CB0 EA00 5D4E \
    41E3 0A9B 6AE6 382B CAAB 3740 346D D9C7 048C B150 B727 EB90 \
    F0A2 AB99 CE12 0482 EB44 DFCB A122 2CF5 91EB 8853 AD87"
INCREMENT f1 flynn 2015.0505 permanent 1 \
    ISSUED=17-jul-2015 SIGN="11A8 C69B 7DC4 114D 95AE 8FD6 010F \
    8A44 DC14 489A 0BB9 74F1 B46F 23CF 7B61 128B 717D 6A25 C52A \
    3E8D 1CAF AD70 9B39 8AA1 012E 50AD 6AA7 2FEB 806F F92C"

2. Start 1 instance of the client application with a vendor defined dup group set to 1
3. Start another instance of the client application with a vendor defined dup group set to 2
4. Start another instance of the client application with a vendor defined dup group set to 3

This will set three processes running with different DUP_GROUPS set. If process three checks in it's license it should be able to check the license back out, but the one of the other 2 process already checks out the license, the license becomes unavailable for process 3

Answer

This issue seems to be caused by the checkout filter and the configuration of the license file. As the features in the license file are all the same apart from the versions, the checkout filter is trying to checkout the version that was requested in the call. Because of this there will always be an attempt to use the first license that is available, even if the process already has a license checked out the checkout filter ignores the DUP_GROUP flag and checks out the first version it comes across, therefore using up more licenses.

One way to resolve this is to modify the checkout filter so that it will only ever checkout the feature and version that it previously checked out and not checkout another versions. The code snippet for this is below:
int LM_CALLBACK_TYPE checkoutCallback(LM_HANDLE_PTR hjob, CONFIG_PTR cfg, PVOID param) {
    CONFIG *config, *pos = 0;
      
    cout << "Checkout post" << endl;

    config = lc_auth_data(hjob, FEATURE);
    if(!config)
        return 0;
    if(strcmp(config->code, gConfigCode) == 0){ 
        printf("\nCheckout Filter: config code (%s) already checkedout.", config->code); 
        printf("\nCheckout Filter: select this config (%s) for checkout.", config->code); 
        return 0; // select this config for checkout 
        }
    return 1;
}



This will require some extra code when the initial checkout occurs to capture the config structure of the checked out license:
checkout(checkoutJob, "2013.0507", &vcode, FEATURE, 1);
    checkedoutconfig = lc_auth_data(checkoutJob, FEATURE); // Get the config of the checked out feature
    if(checkedoutconfig != NULL){
            strcpy(gConfigCode, checkedoutconfig->code); // copy code to global config code for checkout filter
            printf("Code of checked out feature: %s\n", checkedoutconfig->code);
    }



Additional Information

Full source code for the sample application:


#include <stdlib.h>
#include <crtdbg.h>
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <vector>
#include <sstream>
#include <conio.h>

#include "lmclient.h"
#include "lm_attr.h"
#include "activation\include\FlxActSvr.h"

#define HOST \
"@localhost"

#define FEATURE \
"f1"

LM_HANDLE_PTR global_job;

char gConfigCode[21] = {'\0'};
bool checkedout = false;


int LM_CALLBACK_TYPE checkoutCallback(LM_HANDLE_PTR hjob, CONFIG_PTR cfg, PVOID param) {
CONFIG *config, *pos = 0;


cout << "Checkout post" << endl;

config = lc_auth_data(hjob, FEATURE);
if(!config)
return 0;
if(strcmp(config->code, gConfigCode) == 0){
printf("\nCheckout Filter: config code (%s) already checkedout.", config->code);
printf("\nCheckout Filter: select this config (%s) for checkout.", config->code);
return 0; // select this config for checkout
}
return 1;
}

static bool isServerDown = false;

FLEX_ERROR checkout(LM_HANDLE_PTR hjob, char *version, const VENDORCODE_PTR vcode, const char* feature, int quantity) {
FLEX_ERROR rlt;

printf("Pre checkout - code: %s\n", gConfigCode);
if ((rlt = ::lc_checkout(hjob, (const LM_CHAR_PTR)feature, version, quantity, LM_CO_AVAIL_NOWAIT, vcode, LM_DUP_USER)) == LM_NOERROR) {
printf("%s, #%d granted\n", feature, quantity);
}
else {
printf("%s, #%d rejected err:%d\n", feature, quantity, rlt);
}

return rlt;
}


void setAndCreateAttributeJob(LM_HANDLE_PTR *hjob, VENDORCODE vcode)
{
::lc_new_job(0, lc_new_job_arg2, &vcode, hjob);
lc_set_attr(*hjob, LM_A_CHECKOUTFILTER_EX, reinterpret_cast<LM_A_VAL_TYPE>(checkoutCallback));
}

int _tmain(int argc, _TCHAR* argv[])
{
FLEX_ERROR rlt;
VENDORCODE vcode;
LM_HANDLE_PTR checkoutJob;
CONFIG * checkedoutconfig;

struct ::flexinit_property_handle* initHandle(0);
((rlt = ::lc_flexinit_property_handle_create(&initHandle)) == LM_NOERROR);
((rlt = ::lc_flexinit_property_handle_set(initHandle, (FLEXINIT_PROPERTY_TYPE)FLEXINIT_PROPERTY_USE_TRUSTED_STORAGE, (FLEXINIT_VALUE_TYPE)1)) == LM_NOERROR);
((rlt = ::lc_flexinit(initHandle)) == LM_NOERROR);
setAndCreateAttributeJob(&checkoutJob, vcode);
std::ostringstream buf;



buf << "DUP_GROUP_" << argv[1];
std::cout << "checkout ID: " << buf.str() << endl;
FLEX_ERROR rtl = ::lc_set_attr(checkoutJob, LM_A_CHECKOUT_DATA, (LM_A_VAL_TYPE)buf.str().c_str());
checkout(checkoutJob, "2013.0507", &vcode, FEATURE, 1);
checkedoutconfig = lc_auth_data(checkoutJob, FEATURE); // Get the config of the checked out feature
if(checkedoutconfig != NULL){
strcpy(gConfigCode, checkedoutconfig->code); // copy code to global config code for checkout filter
printf("Code of checked out feature: %s\n", checkedoutconfig->code);
}

while (!kbhit()) {
Sleep(5000);
checkout(checkoutJob, "2013.0507", &vcode, FEATURE, 1);
}
lc_free_job(checkoutJob);
::lc_flexinit_cleanup(initHandle);
::lc_flexinit_property_handle_free(initHandle);
::lc_cleanup();
_CrtDumpMemoryLeaks();
return 0;
}
Labels (1)
Was this article helpful? Yes No
No ratings
Version history
Last update:
‎Nov 14, 2018 09:30 PM
Updated by: