The event groups are essentially like a group of semaphores and so the same
reasoning could be applied to semaphores (there are an unknown number of tasks
that may be waiting for the semaphore to be given). Why are semaphores treated
differently in the context of an interrupt when their use is so similar?
With a semaphore it makes no difference how many tasks are actually blocked on a semaphore when it is given – only one task will be unblocked. The task that is unblocked is the highest priority task that is waiting on the semaphore (note this is unlike some systems which just unblock the task that has been waiting the longest – no matter the relative priority of the waiting tasks). The list implementation in FreeRTOS makes this a deterministic operation. You can there therefore calculate a known worst case – which is the case when a task is unblocked, as opposed to the best case when a task is not unblocked (because nothing was waiting for the semaphore).
This is fundamentally different to the case where you use an event group because, unlike with a semaphore, setting an event bit can unblock between 0 and ‘n’ tasks at once, and the value of ‘n’ is not known so as far as the RTOS is concerned there is no calculable worst case – and the worst case may in fact (or is likely in fact) to be too long to be an acceptable operation to do from an interrupt. Again, other systems will do this, but FreeRTOS will not.
The implementation requires the use of an additional task and related overhead, which
is otherwise unnecessary (well in my case it isn’t necessary).
It actually uses the same task as software timers. So if you are using software timers then no additional task is necessary. In your opinion, for your particular application, you suggest the additional task is not necessary. However, in the general case, I would suggest it was necessary for the reasons stated in reply to your first point. If I were to change the code to do everything from the interrupt then more people would be upset than were pleased.
xEventGroupSetBitsFromISR can fail to register the request which in my opinion makes
it not deterministic
You mean the event queue might be full when the bit is set? If so it will probably trigger an assert() – giving you the opportunity to increase the size of the event queue so it doesn’t fail – or in fact handle the failure to post in any way you wish to recover gracefully. There is of course a physical limit to the processing power any particular chip can cope with, so there will be a physical limit to how quickly you can drain the event queue, but that is not unique to that particular queue – the same would be the case for any task processing.